扩展KMP的应用
扩展kmp在字符串匹配专题的应用很广,但很多时候也还是能用kmp、马拉车等字符串匹配算法代替,这篇文章是笔者为18年湖南省赛准备的复习文,文章整理了笔者遇到过的扩展kmp在一些方面的应用
- 题目中出现的字符串名称及其定义:
- 循环同构串: abcd->bcda->cdab->dabc ,将首字符逐一左移至字符串尾部,得到
的所有字符串。 - 后缀子串:Si表示以第i个字符为首的S的后缀字符串。
- 重复不重叠字符串:abcabc,abc重复了两次。书面表示为AAA (A表示字符串)
- 循环同构串: abcd->bcda->cdab->dabc ,将首字符逐一左移至字符串尾部,得到
- 应用分类:
- 定义题:裸题(求T与S的所有后缀子串的最长前缀公共子串长度)
FZU-1901
ZOJ 3587 - 回文前缀子串和回文后缀子串上的应用
HDU 3613 - 分治 + ex_kmp
HUNNU OJ 11565 可惜标程错了
- 定义题:裸题(求T与S的所有后缀子串的最长前缀公共子串长度)
一句总结: 大部分应用都是将自己与反串匹配,就能处理题目要求的字符串性质
一、定义题
FZU-1901
题意:字符串S,问满足S[i] = S[i+P],(i ∈[0,Size -P-1]),升序顺序输出P的取值。
很裸的ex_kmp的定义题!判断Next[j] == Size - j +1 ,以S[j]开头后缀子串是S的一个前缀子串。
/*********************************
*** FZU 1901 ex_kmp 的 Next数组
*********************************/
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <map>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define llt long long
using namespace std;
const int N = 1e6+7777;
int Next[N];
char s[N];
void get_Next(int n){
Next[0] = n;//以s[0]开头后缀子串显然是本串
int i=0;
while(i+1<n&&s[i]==s[i+1])++i;//前面有i+1个数相同
Next[1] = i;
int po = 1;// 到达最远比较位置
for(int i=2;i<n;++i){
if(i+Next[i-po]<Next[po]+po)//不能等于
Next[i] = Next[i-po];
else{
//if(i==9) cout<<i<<endl;
int j = max(0,Next[po]+po-i);
while(i+j<n&&s[j]==s[i+j])++j;
Next[i] = j;
po = i;
}
}
Next[n] = 0;
}
int ans[N];
int main(){
int T;
scanf("%d",&T);
int cas = 0;
while(T--){
scanf("%s",s);
int n = strlen(s);
get_Next(n);
//cout<<Next[9]<<endl;
int cnt = 0;
for(int i=1;i<=n;++i)
if(n-i==Next[i]) ans[++cnt] = i;
printf("Case #%d: %d\n",++cas,cnt);
for(int i=1;i<=cnt;++i)
printf("%d%c",ans[i]," \n"[i==cnt]);
}
return 0;
}
zoj 3587
题意:求S的两个子串拼接成T的方案数。
ex_kmp 统计S的子串与T的前后缀长度为k的子串数。
/*************************************************
*** zoj 3587 拓展kmp的定义题:求出S的后缀子串i
与T的最长前缀k,实际上表示已si
开头的子串段长度<=k,均有一种方案!
同理,找到S反串与T的反串的最长前缀!
*** 实际运用ex_kmp求出与T的前缀长度为k的子串的个数!
*************************************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#define llt long long
#include <map>
#define fi first
#define se second
#define lson (rt<<1)
#define rson ((rt<<1)+1)
using namespace std;
const int N = 1e5+777;
char s[N],t[N];
char ss[N],tt[N];//反串
llt sum[N],a[N];//a 记录 S的子串是T的长度为k的后缀子串的个数
// sum 记录 S的子串是T的长度不超过k的后缀子串的个数和
int Next[N],extend[N];
void getNext(char S[],int n){
Next[0] = n;
int i =0 ;
while(i+1<n&&S[i]==S[i+1]) ++i;
Next[1] = i;
int po = 1;
for(int i=2;i<n;++i){
if(i+Next[i-po]<po+Next[po])
Next[i] = Next[i-po];
else {
int j = max(0,po+Next[po]-i);
while(i+j<n&&S[j]==S[i+j]) ++j;
Next[i] = j;
po = i;
}
}
}
void getExtend(char S[],char T[],int slen,int tlen){
getNext(T,tlen);
int i=0;
int mixn = min(slen,tlen);
while(i<mixn&&S[i]==T[i]) ++i;
extend[0] = i;
int po = 0;
for(int i=1;i<slen;++i){
if(i+Next[i-po]<po+extend[po])
extend[i] = Next[i-po];
else {
int j = max(0,po+extend[po]-i);
while(j<tlen&&i+j<slen&&T[j]==S[i+j]) ++j;
extend[i] = j;
po = i;
}
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s",s);
scanf("%s",t);
int slen = strlen(s);
int tlen = strlen(t);
for(int i=0;i<slen;++i) ss[slen-1-i] = s[i];
for(int i=0;i<tlen;++i) tt[tlen-1-i] = t[i];
/*统计S的子串为T的后缀子串的长度为k的个数*/
memset(a,0,sizeof a);
getExtend(ss,tt,slen,tlen);
for