模板
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");
}