字符串模板

模板

next数组

//下标从0开始
int Next[maxn];//Next[i]是p[0~i-1]的前缀等于后缀的最大长度(下标+1) 
void next_pre(int m){
	int i,j;
	j=Next[0]=-1;//检测到j==-1时就停止迭代
	i=0;
	while(i<m){
		while(-1!=j &&p[i]!=p[j])j=Next[j];
		Next[++i]=++j;
	}
}

kmp

可以统计模式串的出现次数

//洛谷P3375,输出s2在s1中出现的每个位置,答案的下标从1开始
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int nxt[N];
char s1[N], s2[N];
void get_next(char* s,int l, int* nxt) { // s是模式串
    int i = 0, j = -1;
    nxt[0] = -1;
    while(i < l) {
        if(j == -1 || s[i] == s[j])
            nxt[++i] = ++j;
        else j = nxt[j];
    }
}
void kmp(char* p, char* s, int m, int n, int* nxt) { // p是模式串,s是文本串
    int i = 0, j = 0;
    while (i < n) {
        if(j == -1 || s[i] == p[j]) i++, j++;
        else j = nxt[j];
        if(j == m) printf("%d\n", i-m+1), j = nxt[j];
    }
}
int main() {
    scanf("%s%s", s1, s2);
    int len1=strlen(s1);
    int len2=strlen(s2);
    get_next(s2,len2,nxt);
    kmp(s2,s1,len2,len1,nxt);
    for (int i = 1; i <= strlen(s2); i++)
        printf("%d ", nxt[i]);
}
//下标从0开始
int kmpNext[maxn];// kmpNext[i] 的意思:next'[i]=next[next[...[next[i]]]](直到 next'[i]<0 或者 x[next'[i]]!=x[i]) ,在kmp用这种数组更快(用Next也可以)。
void kmp_pre(int m){
	int i,j;
	j=kmpNext[0]=-1;
	i=0;
	while(i<m){
		while(-1!=j && p[i]!=p[j]) j=kmpNext[j];
		if(p[++i]==p[++j])kmpNext[i]=kmpNext[j];
		else kmpNext[i]=j;
	}
}
int kmp(int n,int m){
	int i=0,j=0;
	while(i<n&&j<m){
		if(j==-1 || a[i]==p[j]){//模式串首位就失配后,j==-1
			i++;j++;
		}
		else j=kmpNext[j];
	}
	if(j==m) return i-m;
	else return -1;
}

manachar

char s[maxn],s1[maxn];
int p[maxn];
int manacher(int n){
	int ans=0;
	s1[0]='~';
	s1[1]='#';
	int cnt=1;
	for(int i=0;i<n;i++){
		s1[++cnt]=s[i];
		s1[++cnt]='#';
	}
	int maxr=0,mid=0;
	for(int i=1;i<=cnt;i++){
		if(i<maxr)
			p[i]=min(p[(mid<<1)-i],maxr-i);//对称回文的长度和
		else p[i]=1;//p[i]为半径(包括原点)
		while(s1[i-p[i]]==s1[i+p[i]]){
			p[i]++;
		}
		if(i+p[i]-1>maxr){//看别人都是i+p[i]>maxr,不知道为啥
			maxr=i+p[i]-1;
			mid=i;
		}
		if(p[i]>ans) ans=p[i];
	}
	return ans-1;
}
int main(){
	while(~scanf("%s",s) ){
		int n=strlen(s);
		printf("%d\n",manacher(n));
	}
}

例题

模式串计数

https://cn.vjudge.net/contest/316613#problem/B

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e6+5;
int Next[maxn];//next[i]是p[0~i-1]的前缀等于后缀的最大长度(下标+1) 
char p[maxn];
char a[maxn];
void next_pre(int m){
	int i,j;
	j=Next[0]=-1;
	i=0;
	while(i<m){
		while(-1!=j &&p[i]!=p[j])j=Next[j];
		Next[++i]=++j;
	}
}
int kmpc(int n,int m){
	int i=0,j=0;
	int ans=0;
	while(i<n){
		if(j==-1 || a[i]==p[j]){
			if(j==m-1){
				ans++;
				j=Next[j+1]-1;//模式串指针跳到自身的最大前缀后缀匹配下标
			}
			else{
				i++;j++;
			}
		}
		else j=Next[j];
	}
	return ans;
}
int main(){
	int t,n,m;
	cin>>t;
	while(t--){
		scanf("%s",p);
		scanf("%s",a);
		n=strlen(a);
		m=strlen(p);
		next_pre(m);
		int temp=kmpc(n,m);
		printf("%d\n",temp);
	}
}

Compress Words

https://codeforces.com/contest/1200/problem/E

题意:给定一些字符串,把前缀后缀相同的缩掉拼起来

由于每个串最长有1e6,存储的时候不能直接开一个大二维数组,可以开两个一维数组交替存储

从前往后遍历,把答案储存在ans[]数组中,当下一个字符串长度为l时,从答案串中拿出后l个字符拼到新串之后做一次next[]数组计算,由于start=next[l+l]可能>l,所以要递归到next[start]<=l为止( [0,start-1]和[start,l+l]相同,那么[0,start]的最大前缀后缀相同长度就是整个串的前缀后缀相同长度 )

#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
char p[maxn];
int Next[maxn];//Next[i]是p[0~i-1]的前缀等于后缀的最大长度(下标+1) 
void next_pre(int m){
    int i,j;
    j=Next[0]=-1;//检测到j==-1时就停止迭代
    i=0;
    while(i<m){
        while(-1!=j &&p[i]!=p[j])j=Next[j];
        Next[++i]=++j;
    }
}
char c1[maxn],c2[maxn],ans[maxn];
int l1,l2,len=0;
int main(){
	int n,m,q;
	cin>>n;
	for(int i=1;i<=n;i++){
		if(i&1){
			scanf("%s",c1);
			l1=strlen(c1);
		}
		else{
			scanf("%s",c2);
			l2=strlen(c2);
		}
		if(i>1){
			int temp,start,minn;
			if(i&1){//c1
				for(int j=0;j<l1;j++) p[j]=c1[j];
				for(int j=max(0,len-l1),k=l1;j<len;j++,k++)
					p[k]=ans[j];
				temp=l1+min(l1,len);
				minn=min(l1,len);
			}
			else{
				for(int j=0;j<l2;j++) p[j]=c2[j];
				for(int j=max(0,len-l2),k=l2;j<len;j++,k++)
					p[k]=ans[j];
				temp=l2+min(l2,len);
				minn=min(l2,len);
			}
			next_pre(temp);
			start=Next[temp];
			while(start>minn) start=Next[start];//这是关键
			if(start<=0)start=0;
			if(i&1){
				start=min(start,Next[temp]);
				for(int j=start,k=len;j<l1;j++,k++)
					ans[k]=c1[j];
				len+=l1-start;
			}
			else{
				for(int j=start,k=len;j<l2;j++,k++)
					ans[k]=c2[j];
				len+=l2-start;
			}
		}
		else{
			for(int j=0;j<l1;j++)
				ans[j]=c1[j];
			len=l1;
		}
	}
	for(int j=0;j<len;j++) printf("%c",ans[j]);
	printf("\n");
}
原文地址:https://www.cnblogs.com/ucprer/p/11315945.html