SPOJ - SUBLEX 【后缀自动机】

题目

求第K小子串

题解

建好SAM后,拓扑排序,反向传递后面所形成的串的数量

最后从根开始,按照儿子形成串的数量与k比较走就好了

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
using namespace std;
const int maxn = 200005,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();}
    return out * flag;
}
int pre[maxn],sz[maxn],step[maxn],ch[maxn][26],cnt,last,n;
int a[maxn],b[maxn];
char s[maxn];
void ins(int x){
    int p = last,np = ++cnt;
    last = np; step[np] = step[p] + 1;
    while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
    if (!p) pre[np] = 1;
    else {
        int q = ch[p][x];
        if (step[q] == step[p] + 1) pre[np] = q;
        else {
            int nq = ++cnt; step[nq] = step[p] + 1;
            for (int i = 0; i < 26; i++) ch[nq][i] = ch[q][i];
            pre[nq] = pre[q]; pre[np] = pre[q] = nq;
            while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
        }
    }
}
void walk(int k){
    int u = 1;
    while (k){
        for (int i = 0; i < 26; i++) if (ch[u][i]){
            if (sz[ch[u][i]] >= k){
                putchar(i + ‘a‘);
                k--; u = ch[u][i];
                break;
            }else k -= sz[ch[u][i]];
        }
    }
    puts("");
}
void solve(){
    REP(i,cnt) b[step[i]]++;
    REP(i,cnt) b[i] += b[i - 1];
    REP(i,cnt) a[b[step[i]]--] = i;
    for (int i = cnt; i; i--){
        int u = a[i]; sz[u] = 1;
        for (int j = 0; j < 26; j++)
            if (ch[u][j]) sz[u] += sz[ch[u][j]];
    }
    int Q = read();
    while (Q--) walk(read());
}
int main(){
    scanf("%s",s + 1);
    n = strlen(s + 1); cnt = last = 1;
    REP(i,n) ins(s[i] - ‘a‘);
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8298017.html

时间: 2024-10-10 04:27:03

SPOJ - SUBLEX 【后缀自动机】的相关文章

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

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

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

SPOJ - NSUBSTR 后缀自动机板子

SPOJ - NSUBSTR #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> #define PLI pair<LL, int> #define PDD pair<double,double> #define ull unsigned long long

【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单独算一个子串. 然后

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

Lexicographical Substrings Search \[ Time Limit: 149 ms \quad Memory Limit: 1572864 kB \] 题意 给出一个字符串,求出这个字符串上字典序第 \(k\) 小的子串. 思路 先对给出的字符串构建后缀自动机,因为后缀自动机上从根节点走到任意节点都是原串的一个子串,所以我们可以 \(dfs\) 求出节点 \(i\) 往后存在多少个子串. 对于查询第 \(k\) 小的子串时,在用一个 \(dfs\) 来求,对于当前节点

SPOJ 1812 Longest Common Substring II(后缀自动机)

[题目链接] http://www.spoj.com/problems/LCS2/ [题目大意] 求n个串的最长公共子串 [题解] 对一个串建立后缀自动机,剩余的串在上面跑,保存匹配每个状态的最小值, 取最小值中的最大值即可.由于跑的地方只记录了匹配结尾的状态, 所以还需要更新parent树上的状态,既然匹配到了子节点, 那么parent树链上的值就都能够取到l, 一开始给每个不同状态按照l从小到大分配储存地址, 这样,我们就可以从匹配长度最长的开始更新parent树的情况. [代码] #inc

spoj 1811 LCS - Longest Common Substring (后缀自动机)

spoj 1811 LCS - Longest Common Substring 题意: 给出两个串S, T, 求最长公共子串. 限制: |S|, |T| <= 1e5 思路: dp O(n^2) 铁定超时 后缀数组 O(nlog(n)) 在spoj上没试过,感觉也会被卡掉 后缀自动机 O(n) 我们考虑用SAM读入字符串B; 令当前状态为s,同时最大匹配长度为len; 我们读入字符x.如果s有标号为x的边,那么s=trans(s,x),len = len+1; 否则我们找到s的第一个祖先a,它

spoj 1812 LCS2 - Longest Common Substring II (后缀自动机)

spoj 1812 LCS2 - Longest Common Substring II 题意: 给出最多n个字符串A[1], ..., A[n], 求这n个字符串的最长公共子串. 限制: 1 <= n <= 10 |A[i]| <= 1e5 思路: 和spoj 1811 LCS差不多的做法 把其中一个A建后缀自动机 考虑一个状态s, 如果A之外的其他串对它的匹配长度分别是a[1], a[2], ..., a[n - 1], 那么min(a[1], a[2], ..., a[n - 1]

【SPOJ】8222. Substrings(后缀自动机)

http://www.spoj.com/problems/NSUBSTR/ 题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值.求F(1)..F(Length(S)) 这题做法: 首先建立字符串的后缀自动机. 因为自动机中的每个状态都代表一类子串前缀,且任意状态的最长的|max|所对应的子串是唯一的. 所以我们算出每个子串(即找到的状态是end态),他们的right值为++(即保证长度为当前子串的出现次数为1),然后自底向上在parent树中更新right值(pare

【后缀自动机】SPOJ 1812-LCSII

题意: 给出最多10个长度不超过100000的字符串,求他们的LCS的长度.时限是鬼畜的0.25s . 后缀自动机练习...虽然有人这么说但我并不觉得hash能过. 本题可以说是[论SAM中按step排序更新pre的重要性]: 总的来说做法和1811-LCS有点类似,不同的是因为有多个字符串,因此每个字符串都需要在SAM上跑一次. 记录下每个节点最长能容纳长度为多少的字符,最后取个MAX就行了. 用nans[i]表示匹配当前字符串时,i号点能容纳的子串长度,如果i的pre上的当前答案比i还要小的