您的位置:首页 > 其它

SPOJ1811 后缀自动机入门

2017-03-16 00:06 309 查看
题目链接点我点我:-)

题目描述

求两个字符串的最长公共子串的长度,字符串长度小于等于5∗105

输入格式

两行即两个字符串

输出格式

一个整数,表示两个字符串的最长公共子串的长度

思路

后缀自动机裸题,入门的一个好的讲解:传送门

感想

还是有一些地方不是很理解,特别是匹配的时候有点乱(关于step的问题)

还有一个同学的习题锦集,可以去做一下:传送门

(ps: 下方程序中的Query函数并没有用)

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define N (500000+5)

struct Suffix_Automaton{
char s
;
int son
[26], pre
, step
, root, last, Tot;

void Insert(char ch){
ch -= 'a';

int p = last, np = ++Tot;
step[np] = step[last]+1; last = np;

for(;p&&!son[p][ch]; p=pre[p]) son[p][ch] = np;
if(!p) pre[np] = root;
else{
if(step[p]+1 == step[son[p][ch]]) pre[np] = son[p][ch];
else{
int q = son[p][ch], nq = ++Tot;

pre[nq] = pre[q]; step[nq] = step[p]+1;
For(i, 0, 25) son[nq][i] = son[q][i];
pre[q] = pre[np] = nq;
for(;son[p][ch]==q; p=pre[p]) son[p][ch] = nq;
}
}
}

int Query(char *s){
int nc, now = root, len = strlen(s);
For(i, 0, len-1){
nc = s[i]-'a';
if(!son[now][nc]) return i;
now = son[now][nc];
}
return len;
}
}sat;

char s1
, s2
;

int main(){
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif

sat.root = sat.last = ++sat.Tot;
scanf("%s%s", s1, s2);

int len1 = strlen(s1), len2 = strlen(s2);
For(i, 0, len1-1) sat.Insert(s1[i]);

int ans = 0, len = 0, now = sat.root;
char ch;
For(i, 0, len2-1){
ch = s2[i]-'a';

if(sat.son[now][ch]) ++len, now = sat.son[now][ch];
else{
while(now && !sat.son[now][ch]) now = sat.pre[now];
if(!now) now = sat.root, len = 0;
else len = sat.step[now]+1, now = sat.son[now][ch];
}
ans = max(ans, len);
}
printf("%d\n", ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: