zoukankan      html  css  js  c++  java
  • 前缀函数 从KMP到AC自动机

    -1.参考资料

    OI wiki

    0.定义

    本文中,字符串下标均从1开始,所以可能和 OI wiki 有一些出入。

    定义(pi[i]) (前缀函数)表示 (s[1dots i]) 的最长公共前缀后缀的长度。

    形式化的,有:

    [pi[i]=max_{k=0dots i}{k|s[1dots k]=s[i-(k-1)dots i]} ]

    特别的,(pi[1]=0)

    1.预处理

    首先有一个显然的结论:

    [pi[i+1]le pi[i]+1 ]

    原因是 (s[1dots pi[i+1]]=s[i-(pi[i+1]-1)dots i]) 可以推出 (s[1dots pi[i+1]-1]=s[i-(pi[i+1]-1)dots i-1])

    (k=pi[i+1]-1) 即得 (s[1dots k]=s[i-1-(k-1)dots i-1])

    根据 (pi[i]) 的定义即得 (pi[i]ge k=pi[i+1]-1),得证。

    于是考虑暴力枚举预处理,然后利用字符串哈希来判别字符串是否相等。

    复杂度 (O(sum_{i=1}^{n-1}pi[i]+1-pi[i]-1)=O(n))

    但是这个算法不一定是正确的,可能会被卡模数,我们考虑继续优化。

    首先根据上面那个结论我们可以得到一个推论,就是 (s[1dots pi[i+1]-1]=s[i-(pi[i+1]-1)dots i-1]) ,因此我们考虑枚举 (s[1dots i-1]) 的公共前缀后缀。

    考虑怎么 (O(1)) 找到一个公共前缀后缀的前驱(i.e.仅次于这个公共前缀后缀的第二长度)。

    设已知 (s[1dots j]=s[i-j+1dots i]),要求出 (max{j'|s[1dots j']=s[i-j'+1dots i],j'<j})

    [s[1dots j']=s[i-j'+1dots i]=s[j-j'dots j] ]

    我们发现了

    [s[1dots j']=s[j-j'dots j] ]

    所以

    [j'=pi[j] ]

    Q.E.D.

    转移的次数仍然是 (O(n)) 的,但是因为我们避免了字符串比较,所以总复杂度 (O(n))

    代码:

    void init(char*s,int n,int*nxt){
    	nxt[1]=0;
    	for(int i=2,j;i<=n;i++){
    		while(j&&s[j+1]!=s[i])j=nxt[j];
    		if(s[i]==s[j+1])j++;
    		nxt[i]=j;
    	}
    }
    
  • 相关阅读:
    【GDOI 2016 Day1】第二题 最长公共子串
    2016.5.21【初中部 NOIP提高组】模拟赛A​ 总结
    【GDOI2014模拟】雨天的尾巴
    树链剖分
    GDOI2016总结
    【GDOI 2016 Day2】第一题 SigemaGO
    【ZJOI2008】树的统计
    【GDOI2016模拟4.22】总结
    【NOIP2016提高A组模拟7.17】寻找
    【NOIP2016提高A组模拟7.17】锦标赛
  • 原文地址:https://www.cnblogs.com/happydef/p/kmp.html
Copyright © 2011-2022 走看看