SPOJ LCS题目大意:
给定两个字符串,求他们的最长公共子串。
由于本蒟根本不懂后缀自动机,所以借着kuangbin dalao的代码,认真理解了如何初步黑箱操作后缀自动机~
首先,你需要获得dalao的后缀自动机板子~
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int CHAR = 26;
const int N = 250010;
struct SAM_Node{
SAM_Node *fa, *next[CHAR];
int len;
int id, pos;
SAM_Node(){}
SAM_Node(int _len){
fa = 0;
len = _len;
memset(next,0,sizeof(next));
}
};
SAM_Node pool[N<<1], *root, *last;
int size;
SAM_Node *nnode(int len){
pool[size] = SAM_Node(len);
pool[size].id = size;
return &pool[size++];
}
SAM_Node *nnode(SAM_Node *p){
pool[size] = *p;
pool[size].id = size;
return &pool[size++];
}
void SAM_init(){
size = 0;
root = last = nnode(0);
pool[0].pos = 0;
}
void add(int x,int len){
SAM_Node *p = last, *np = nnode(p->len + 1);
np->pos = len;
last = np;
for(; p && !p->next[x];p = p->fa)
p->next[x] = np;
if(!p){
np->fa = root;
return;
}
SAM_Node *q = p->next[x];
if(q->len == p->len + 1){
np->fa = q;
return;
}
SAM_Node *nq = nnode(q);
nq->len = p->len + 1;
q->fa = nq;
np->fa = nq;
for(; p && p->next[x] == q; p = p->fa)
p->next[x] = nq;
}
void SAM_build(char *s){
SAM_init();
int len = strlen(s);
for(int i = 0;i < len;i++)
add(s[i] - 'a', i+1);
}
看起来真的是功能一应俱全呐~
然后在SAM_build里边传进去一个字符串,就可以获得一台后缀自动机!
顺便安利一位dalao搞出来的好东西(用它做了不少图)
其实多用几个字符串做做图试试,也就明白后缀自动机大概是个什么东西啦~
那后缀自动机长什么亚子呢?
比如字符串abab,它的后缀自动机就是这个亚子:
图中的黑色箭头,就是我们SAM_Node里的 *next 啦~
红色箭头,就是 *fa 啦~
next顾名思义,那fa呢?fa其实就是失配指针吧(大胆揣测)。
每当不能匹配的时候,就顺着红色箭头往上找~
对于这一题的话,记得记录最长的长度鸭~
后缀自动机里顺着各种箭头,可以找到自动机字符串的每一个子串。而当匹配的时候,就是把第二个字符串的每一个前缀(塞的话就是每一位)依次塞到自动机里,用前缀的一个个后缀匹配第一个字符串的一个个子串~
酱紫就完成了每个子串的匹配啦~
省时间省空间的话,还是要感谢后缀自动机里花里胡哨的指针啊~
于是就有了酱紫的主程序啦
注释是我乱加的,有错的话还请各位dalao不辞雅正~
char str1[N], str2[N];
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(scanf("%s%s",str1,str2) == 2){
SAM_build(str1);//以字符串1建立后缀自动机
int len = strlen(str2);
SAM_Node *tmp = root;
int ans = 0;
int t = 0;//匹配长度
for(int i = 0;i < len;i++){ //对于字符串2的每一个前缀做后缀匹配
if(tmp->next[str2[i]-'a']){ //如果可以从上一步往下走(是相同后缀)
tmp = tmp->next[str2[i]-'a'];
t++;//往下走,长度+1
}
else{
while(tmp && !tmp->next[str2[i]-'a'])//tmp不为空且能走第i位
tmp = tmp->fa; //沿着失配指针向上,寻找后缀(去掉一位前缀)
if(tmp == NULL){ //失配到底了。。
tmp = root;
t = 0;
}
else{
t = tmp->len + 1;//tmp节点代表的最小长度
tmp = tmp->next[str2[i]-'a'];
}
}
ans = max(ans,t);
}
printf("%d\n",ans);
}
return 0;
}