这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 | |||
|
2020-2021:teams:legal_string:王智彪:后缀自动机 [2021/07/30 16:08] 王智彪 |
2020-2021:teams:legal_string:王智彪:后缀自动机 [2021/07/30 17:04] (当前版本) 王智彪 |
||
|---|---|---|---|
| 行 229: | 行 229: | ||
| </hidden> | </hidden> | ||
| + | |||
| + | 8.最长公共子串 | ||
| + | |||
| + | 后缀数组基本操作。 | ||
| + | |||
| + | 后缀自动机做法:对 $S$ 构造后缀自动机,处理字符串 $T$ ,对于每一个前缀,都在 $S$ 中寻找这个前缀的最长后缀。显然所有这样的答案取最大值就是最终的答案了。 | ||
| + | |||
| + | 我们维护两个变量:当前状态 $v$ 和当前长度 $l$ 。最开始 $v=t_{0}$ 且 $l=0$ 。 | ||
| + | |||
| + | 如果存在从 $v$ 到字符 $c$ 的转移,我们只需要转移并且让 $l++$ 。 | ||
| + | |||
| + | 如果不存在只能让状态一直跳 $link$ ,并且缩短长度,赋值为当前状态的长度 $len(v)$ 即可。 | ||
| + | |||
| + | 如果仍不存在,则必然到达 $-1$ ,这时我们重新让 $v=0,l=0$ 。 | ||
| + | |||
| + | 这样我们最多移动两倍 $|T|$ ,所以时间复杂度就是 $O(|T|)$ 。 | ||
| + | |||
| + | <hidden 实现> | ||
| + | |||
| + | <code cpp> | ||
| + | |||
| + | void work_lcs(char s1[],char s2[]) { | ||
| + | ans=0,anspos=-1; | ||
| + | int l1=strlen(s1),l2=strlen(s2); | ||
| + | for(int i=0;i<l1;i++) { | ||
| + | add(s1[i]-'a',i+1); | ||
| + | } | ||
| + | int v=1,l=0; | ||
| + | for(int i=0;i<l2;i++) { | ||
| + | while(v!=1&&!dian[v].ch[s2[i]-'a']) { | ||
| + | v=dian[v].fa; | ||
| + | l=dian[v].len; | ||
| + | } | ||
| + | if(dian[v].ch[s2[i]-'a']) { | ||
| + | v=dian[v].ch[s2[i]-'a']; | ||
| + | l++; | ||
| + | } | ||
| + | if(l>ans) { | ||
| + | ans=l; | ||
| + | anspos=i; | ||
| + | } | ||
| + | } | ||
| + | //printf("%d %d\n",ans,anspos); | ||
| + | if(anspos==-1) { | ||
| + | puts("no lcs!"); | ||
| + | } | ||
| + | else { | ||
| + | for(int i=anspos-ans+1;i<=anspos;i++) { | ||
| + | putchar(s2[i]); | ||
| + | } | ||
| + | putchar(10); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </code> | ||
| + | |||
| + | </hidden> | ||
| + | |||
| + | 剩下在oi-wiki上的例题之后再补吧... | ||