字符串的各种算法

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/85258735

之前想写多项式的各种算法,然后被FFT多项式求逆打自闭了。
临表涕零,不知所言。

就按我学的顺序来吧。

  1. m a n a c h a r manachar
    可求出对于每个点 i i ,以 i i 为中心的最长回文串的右端点到i的总字符数 l c [ i ] lc[i] (即 i + l c [ i ] 1 i+lc[i]-1 为最长回文串的右端点)。
    如果在字符串每两个字符间插入一个 * 字符,那么也可以计算长度为偶数的回文串(没有中心)
    发现 i i 如果在另一个回文串的右半部分,即存在 j j 使得 j < i j<i 并且 j + l c [ j ] > i j+lc[j]>i ,那么 l c [ i ] > = m i n ( l c [ 2 j i ] , j + l c [ j ] i ) lc[i] >= min(lc[2*j-i],j+lc[j]-i) (因为回文串的性质,在这个回文串中才满足)。
    那么我们从小到大计算 l c [ i ] lc[i] ,保存 1 1 i 1 i-1 的最远的回文串的右端点 m x mx 和其回文串中心 o o ,那么如果 i > m x i>mx 那么就暴力增加 l c [ i ] lc[i] 并验证,否则就让 l c [ i ] = m i n ( l c [ 2 o i ] , m x i + 1 ) lc[i]=min(lc[2*o-i],mx-i+1) 然后再暴力增加 l c [ i ] lc[i] 并验证。
    发现每次暴力的成功增加都会导致 m x mx 增大。。。。。。
    所以只会暴力 O ( n ) O(n) 次。
    时间复杂度 O ( n ) O(n)

    模板:luogu 3805

#include<bits/stdc++.h>
#define maxn 22000005
using namespace std;

int lc[maxn];
char s[maxn];

void manacher(char s[maxn],int lc[maxn])
{
	int tot=0;
	static char ns[maxn]={};
	ns[tot] = '&' , ns[++tot] = '*'; // 首尾增加奇怪的字符,那么怎么扩展都会在边界时失败,省去了判断边界
	for(int i=0,len=strlen(s);i<len;i++) ns[++tot] = '*' , ns[++tot] = s[i];
	ns[++tot] = '*' , ns[++tot] = '%';
	
	int mx = 0 , o = 0;//
	for(int i=2;i<tot-1;i++)
	{
		if(i < mx) lc[i] = min(lc[2*o-i],mx-i);
		else lc[i] = 1;
		for(;ns[i-lc[i]] == ns[i+lc[i]];lc[i]++);
		if(i+lc[i] > mx) mx = i + lc[i] , o = i;//实际取右端点+1更方便
	}
}

int main()
{
	scanf("%s",s);
	manacher(s,lc);
	printf("%d",*max_element(lc,lc+(strlen(s)<<1)+1)-1);
}
  1. K M P KMP
    最大的特点就是 n x t nxt 数组,其他的都是这个数组的用途而已。
    n x t [ i ] nxt[i] 为 最大的 j j 满足字符串 s [ 0 , j 1 ] = s [ i j , i 1 ] s[0,j-1] = s[i-j,i-1]
    这个 n x t [ i ] nxt[i] 也可以像 m a n a c h e r manacher 一样利用之前的答案。
    如果 s [ i 1 ] = = s [ n x t [ i 1 ] ] s[i-1] == s[nxt[i-1]] 那么 n x t [ i ] = n x t [ i 1 ] + 1 nxt[i]=nxt[i-1]+1
    否则就看是否有 s [ i 1 ] = = s [ n x t [ n x t [ i 1 ] ] ] s[i-1] == s[nxt[nxt[i-1]]] 有就 n x t [ i ] = n x t [ n x t [ i 1 ] ] + 1 nxt[i]=nxt[nxt[i-1]]+1
    一直算下去。
    发现每次nxt最多增加1,减少至少减少1,那么总共就只会增加减少 O ( n ) O(n) 次。
    时间复杂度 O ( n ) O(n)
    而这个数组的主要用途是拿来求一个串A在另一个串B中出现了多少次的。
    我们把A的 n x t nxt 数组求出。
    然后从小到大枚举i,维护以i结尾的B串最多匹配A串的前k位,发现这个可以用 n x t nxt 优化,如果B[i] = A[k] 那么 i结尾可以匹配前k位,否则k=nxt[k]继续比较。。。。。 k = A k=|A| 时匹配成功。
    模板:luogu P3375
#define maxn 1000005
using namespace std;

char s1[maxn],s2[maxn];

void KMP(char s1[maxn],char s2[maxn])
{
	static int nxt[maxn];
	int n = strlen(s1) , m = strlen(s2);
	
	nxt[0] = -1;
	for(int i=0,j=-1;i<m;)
		if(j==-1 || s2[i] == s2[j]) nxt[++i] = ++j;
		else j = nxt[j];
			
	for(int i=0,j=0;i<=n;i++,j++)
	{
		if(j == m) printf("%d\n",i-m+1) , j = nxt[j];
		if(i<n) while(j!=-1 && s1[i]!=s2[j]) j = nxt[j];
	}
	
	for(int i=1;i<m;i++)
		printf("%d ",nxt[i]);
	printf("%d\n",nxt[m]);
}

int main()
{
	scanf("%s\n%s",s1,s2);
	KMP(s1,s2);
}
除此之外,nxt数组还有很多的妙用。
但和manacher一样,和别的套起来考就是一道好题
例【POJ 3167】Cow Patterns (KMP+树状数组)
  1. 最小表示法 待填 比较好的博客
  2. exkmp图文并茂的博客
  3. AC 自动机
  4. 回文自动机
  5. 后缀自动机
  6. SA后缀数组
  7. 后缀树(我想打删除线)
  8. Trie(我好像遗忘了什么)

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/85258735