字符串

字符串

KMP

\(p_i\)表示\(s_{1...i}\)的最长真前缀,真后缀(“真”即是不包括原串)相等

处理就很简单,每个i就判断能否更新i-1的答案,如不行就i变成\(p_{i-1}\)再处理

Fu(i,2,m+n+1){
	int j=p[i-1];
	while(j>0&&a[i]!=a[j+1]) j=p[j];
	if(a[i]==a[j+1]) p[i]=j+1;
}

EXKMP

\(p_i\)表示\(s_{0...n}\)\(s_{i...n}\)的LCP长度

对于i,考虑一个\(s_{l,r}\)为s的最大的前缀(l<i)

i<=r时,则\(s_{i..r}=s_{i-l,r-l}\),即\(p_i\geq min(p_{i-l},r-i+1)\)

然后就可以暴力扩展,更新l,r

时间复杂度可以保证,因为每次暴力扩展,r都会更新

Fu(i,1,n+m){
	if(i<=r) p[i]=min(p[i-l],r-i+1);
	while(b[p[i]]==b[i+p[i]]) p[i]++;
	if(i+p[i]-1>r) l=i,r=i+p[i]-1;
}

Manacher

大概思路就是现在已经确定了一个最右边的回文串,中心为mm,最右边为mr

我们枚举的以i为中心的回文串,若在最右边的回文串内,那么发现它一定有个对称的左边,然后处理一下

否则就重头开始匹配

时间复杂度:发现每次匹配mr都会变大,所以是\(O(n)\)

Fu(i,2,n*2){
	int k=i>mr?1:min(d[mm*2-i],mr-i+1);
	while(s[i+k]==s[i-k]&&i+k<=n*2+1) k++;
	d[i]=k;
	if(mr<i+k-1) mr=i+k-1,mm=i;
	ans=max(ans,k-1);
}

AC自动机

先把trie树建出来,然后类似建kmp

void getfail(){
	q[1]=0;
	while(l<r){
		l++;
		Fu(i,0,25){
			int u=trie[q[l]].son[i];
			if(u){
				q[++r]=u;
				trie[u].fail=l==1?0:trie[trie[q[l]].fail].son[i];
			}
			else trie[q[l]].son[i]=trie[trie[q[l]].fail].son[i];
		}
	}
}
void find(){
	int o=0;
	Fu(i,1,n){
		o=trie[o].son[s[i]-'a'];
		int u=o;
		while(u&&trie[u].bj!=-1){
			ans+=trie[u].bj;
			trie[u].bj=-1;
			u=trie[u].fail;
		}
	}
}
posted @ 2024-02-22 20:47  zhy_learn  阅读(3)  评论(0编辑  收藏  举报