Lexicographical Substring Search SPOJ - SUBLEX (后缀自动机)

Lexicographical Substrings Search

\[
Time Limit: 149 ms \quad Memory Limit: 1572864 kB
\]

题意

给出一个字符串,求出这个字符串上字典序第 \(k\) 小的子串。

思路

先对给出的字符串构建后缀自动机,因为后缀自动机上从根节点走到任意节点都是原串的一个子串,所以我们可以 \(dfs\) 求出节点 \(i\) 往后存在多少个子串。
对于查询第 \(k\) 小的子串时,在用一个 \(dfs\) 来求,对于当前节点\(u\), 从\(1-26\) 枚举下一步的方案 \(v\),如果从 \(v\) 往后的子串比 \(k\) 少,那么不用往下走,直接 \(k-=cnt[v]\)。否则往下走一步,让 \(k--\)。如此一直走到 \(k=0\) 为止。

/***************************************************************
    > File Name    : a.cpp
    > Author       : Jiaaaaaaaqi
    > Created Time : 2019年05月22日 星期三 17时06分12秒
 ***************************************************************/

#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  lowbit(x)  x & (-x)
#define  mes(a, b)  memset(a, b, sizeof a)
#define  fi         first
#define  se         second
#define  pii        pair<int, int>

typedef unsigned long long int ull;
typedef long long int ll;
const int    maxn = 1e5 + 10;
const int    maxm = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;
const double pi   = acos(-1.0);
const double eps  = 1e-8;
using namespace std;

int n, m;
int cas, tol, T;

struct SAM {
    struct Node{
        int next[27];
        int fa, len;
        void init() {
            mes(next, 0);
            len = fa = 0;
        }
    } node[maxn<<1];
    int sz, last;
    int cnt[maxn<<1];
    void init() {
        sz = last = 1;
        node[sz].init();
        mes(cnt, 0);
    }
    void insert(int k) {
        int p = last, np = last = ++sz;
        node[np].init();
        node[np].len = node[p].len+1;
        for(; p&&!node[p].next[k]; p=node[p].fa)
            node[p].next[k] = np;
        if(p==0) {
            node[np].fa = 1;
        } else {
            int q = node[p].next[k];
            if(node[q].len == node[p].len + 1) {
                node[np].fa = q;
            } else {
                int nq = ++sz;
                node[nq] = node[q];
                node[nq].len = node[p].len + 1;
                node[np].fa = node[q].fa = nq;
                for(; p&&node[p].next[k]==q; p=node[p].fa)
                    node[p].next[k] = nq;
            }
        }
    }
    void dfs(int u) {
        if(cnt[u])  return ;
        cnt[u] = 1;
        for(int i=1; i<=26; i++) {
            int v = node[u].next[i];
            if(v) {
                dfs(v);
                cnt[u] += cnt[v];
            }
        }
    }
    char ss[maxn];
    void find(int u, int k, int len) {
        if(k==0) {
            ss[len] = '\0';
            printf("%s\n", ss+1);
            return ;
        }
        for(int i=1; i<=26; i++) {
            int v = node[u].next[i];
            if(v==0)    continue;
            if(cnt[v] < k)  k -= cnt[v];
            else {
                k--;
                ss[len] = 'a'+i-1;
                find(v, k, len+1);
                return ;
            }
        }
    }
    void solve(int k) {
        find(1, k, 1);
    }
} sam;
char s[maxn];

int main() {
    sam.init();
    scanf("%s", s+1);
    int len = strlen(s+1);
    for(int i=1; i<=len; i++) {
        sam.insert(s[i]-'a'+1);
    }
    sam.dfs(1);
    // for(int i=2; i<=sam.sz; i++) {
    //     printf("cnt[%d] = %d\n", i, sam.cnt[i]);
    // }
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &m);
        sam.solve(m);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/10907687.html

时间: 2024-10-11 20:46:47

Lexicographical Substring Search SPOJ - SUBLEX (后缀自动机)的相关文章

Lexicographical Substring Search SPOJ - SUBLEX (后缀数组)

Lexicographical Substrings Search \[ Time Limit: 149 ms \quad Memory Limit: 1572864 kB \] 题意 给出一个字符串,求出这个字符串上字典序第 \(k\) 小的子串. 思路 对于给出的字符串,求出后缀数组,根据后缀数组的 \(height\) 数组,我们可以很容易得到一个字符的总子串个数是 \(\sum_{i=1}^{n} (n-sa[i]+1+height[i])\),利用这个式子,就可以求出第 \(k\) 小

【SPOJ】7258. Lexicographical Substring Search(后缀自动机)

http://www.spoj.com/problems/SUBLEX/ 后缀自动机系列完成QAQ...撒花..明天or今晚写个小结? 首先得知道:后缀自动机中,root出发到任意一个状态的路径对应一个子串,而且不重复.(原因似乎是逆序后缀树? 所以我们在自动机上预处理每一个状态的子串数目,然后从小到大枚举字符. 子串数目可以这样预处理出:s[x]=sum{s[y]}+1, y是x出发的下一个点,意思就是说,以x开头的子串有那么多个(即将孩子的所有子串前边都加上x),然后x单独算一个子串. 然后

SPOJ SUBLEX 7258. Lexicographical Substring Search

看起来像是普通的SAM+dfs...但SPOJ太慢了......倒腾了一个晚上不是WA 就是RE ..... 最后换SA写了...... Lexicographical Substring Search Time Limit: 1000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu [Submit]   [Go Back]   [Status] Description Little Daniel loves to play wi

SPOJ SUBLEX - Lexicographical Substring Search

SUBLEX - Lexicographical Substring Search no tags Little Daniel loves to play with strings! He always finds different ways to have fun with strings! Knowing that, his friend Kinan decided to test his skills so he gave him a string S and asked him Q q

【spoj7528】 Lexicographical Substring Search

http://www.spoj.com/problems/SUBLEX/ (题目链接) 题意 给出一个字符串,询问其中字典序第K小的子串. Solution 后缀自动机例题. 构出后缀自动机以后,对每个节点预处理出从这个节点可以到达多少个不同的子串.然后就是类似于在平衡树上查找一样沿着SAM一路查找下去一路更新答案了. 代码借鉴的DaD3Zz大神,感觉好优美. 细节 后缀自动机的节点数是2N的. 代码 // spoj7528 #include<algorithm> #include<io

SPOJ LCS 后缀自动机找最大公共子串

这里用第一个字符串构建完成后缀自动机以后 不断用第二个字符串从左往右沿着后缀自动机往前走,如能找到,那么当前匹配配数加1 如果找不到,那么就不断沿着后缀树不断往前找到所能匹配到当前字符的最大长度,然后将cur节点转移到当前节点即可,再把答案加1 记住不断更新所能得到的最大值 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using

SPOJ SUBLEX Lexicographical Substring Search - 后缀数组

题目传送门 传送门I 传送门II 题目大意 给定一个字符串,多次询问它的第$k$大本质不同的子串,输出它. 考虑后缀Trie.依次考虑每个后缀新增的本质不同的子串个数,显然,它是$n - sa[i] - height[i]$. 求出$height$数组后,求一求本质不同的子串个数的前缀和,可以对每个询问二分. 这里可以直接离线,$O(n + m)$扫一扫就好了. Code 1 /** 2 * SPOJ 3 * Problem#SUBLEX 4 * Accepted 5 * Time: 30ms

SPOJ 7258 Lexicographical Substring Search(后缀自动机)

[题目链接] http://www.spoj.com/problems/SUBLEX/ [题目大意] 给出一个字符串,求其字典序排名第k的子串 [题解] 求出sam上每个节点被经过的次数,然后采用权值线段树上查找第k大数类似的方法, 每次确定查找范围,进入相应的子树,同时输出路径上的点即可. [代码] #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const

spoj LCS 后缀自动机

链接:http://www.spoj.com/problems/LCS/ 题意两串LCS 确实没什么好说的,第一次编嘛.把网上的教程斗翻出来看一遍就好了. 另发现,百度内部用户交流使用的图片在百度快照中看得到. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 2510000 #define M