https://blog.csdn.net/qq_37164003/article/details/77424776
讲了一下扩展kmp与kmp中数组的含义。
https://www.cnblogs.com/dilthey/p/8620119.html
重点讲的扩展kmp
https://blog.csdn.net/dl962454/article/details/79910744 详解
https://blog.csdn.net/qq_40938077/article/details/80460853
https://blog.csdn.net/liujiuxiaoshitou/article/details/70232219
http://www.cnblogs.com/chenxiwenruo/p/3546457.html循环节
https://vjudge.net/contest/240809#problem/A习题链接
Number Sequence
HDU - 1711
//这道题的next数组是由优化的next数组写的,next数组的含义不是指最长前缀与后缀的长度,因为加了递归优化,值一般就是-1与0.
//求最开始的匹配位置。(题目意思)
#include
#include
#include
using namespace std;
const int maxm = 1e6 + 5;
int a[maxm], b[maxm], nex[maxm];
int t, n, m, x;
void getnext() {
nex[0] = -1;
int k = -1;
int j = 0 ;
while(j < m) {
if(k == -1 || b[j] == b[k]) {
j++, k++;
if(b[j] != b[k]) nex[j] = k;
else nex[j] = nex[k];
}
else k = nex[k];
}
}
int kmps() {
int i = 0, j = 0;
getnext();
while(i < n && j < m) { //只有这个题目中需要j < m这个条件,因为当==m是就需要输出了。
if(j == -1 || a[i] == b[j]) {
i++, j++;
}
else j = nex[j];
// printf("cbuisdc\n");
}
if(j == m) return i - j + 1;
return -1;
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
for(int i = 0; i < m; i++) {
scanf("%d", &b[i]);
}
if(kmps() == -1) printf("-1\n");
else printf("%d\n", kmps());
}
return 0;
}
Period
HDU - 1358
![kmp算法_第1张图片](http://img.e-com-net.com/image/info8/e882eb0c352a413997ed251ee7e0547b.jpg)
从这一题把next数组输出可知,next数组值该字符串的最长相同前缀与后缀,而且是包含了本身的。(如果这么说的话,相当于把字符串下标从1开始)
//这个是没加递归优化的next数组,指的是最长前缀与后缀的值。
//题目意思是指在i之前的字符串是不是有循环节的,有的话就输出循环节的长度与循环节的个数。
#include
#include
#include
using namespace std;
const int maxm = 1e6 + 5;
int n;
int nex[maxm];
void getnext(char ch[], int m) {
memset(nex, 0, sizeof(nex));
nex[0] = -1;
int j = 0, k = -1;
while(j < m) {
//k表示前缀,j表示后缀。
if(k == -1 || ch[j] == ch[k]) {
j++;
k++;
nex[j] = k;
}
else k = nex[k];
}
//for(int i = 0; i <= m; i++) {
// printf("%d \n", nex[i]);
//}
//printf("\n");
}
char ch[maxm];
int main() {
int cnt = 0;
while(~scanf("%d", &n) && n) {
scanf("%s", ch);
getnext(ch, n);
printf("Test case #%d\n", ++cnt);
for(int i = 2; i <= n; i++) {
int j = i - nex[i];
if(j != i) {
if(i % j == 0) {
printf("%d %d\n", i, i / j);
}
}
}
printf("\n");
}
return 0;
}
Simpsons’ Hidden Talents
HDU - 2594
https://blog.csdn.net/sunyutian1998/article/details/82960588
扩展kmp板子题
定义母串S,和字串T,设S的长度为n,T的长度为m,求T与S的每一个后缀的最长公共前缀,
也就是说,设extend数组,extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i
#include
using namespace std;
const int maxn=5e4+10;
int nxtt[maxn],nxts[maxn];
//nett是记录模式串的next值,nxts是最终的结果。
int l1,l2;
char ch1[maxn],ch2[maxn];
void exkmp(char *t,int lt)
{
int i=0,j,po;
nxtt[0]=lt;//初始化next[0]
while(t[i]==t[i+1]&&i+1//计算next[1]
i++;
nxtt[1]=i;
po=1;//初始化po的位置
for(i=2;i)
{
if(nxtt[i-po]+i//第一种情况,可以直接得到next[i]的值
nxtt[i]=nxtt[i-po];
else//第二种情况,要继续匹配才能得到next[i]的值
{
j=nxtt[po]+po-i;
if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
while(i+j//计算next[i]
j++;
nxtt[i]=j;
po=i;//更新po的位置
}
}
}
void solve(char *s,int ls,char *t,int lt)
{
int i=0,j,po;
while(s[i]==t[i]&&i//计算ex[0]
i++;
nxts[0]=i;
po=0;//初始化po的位置
for(i=1;i)
{
if(nxtt[i-po]+i//第一种情况,直接可以得到ex[i]的值
nxts[i]=nxtt[i-po];
else//第二种情况,要继续匹配才能得到ex[i]的值
{
j=nxts[po]+po-i;
if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
while(i+j//计算ex[i]
j++;
nxts[i]=j;
po=i;//更新po的位置
}
}
}
int main()
{
int i,ans;
while(scanf("%s%s",ch1,ch2)!=EOF)
{
l1=strlen(ch1),l2=strlen(ch2);
//ch1是模式串,ch2是文本串。
exkmp(ch1,l1);
solve(ch2,l2,ch1,l1);
ans=0;
for(i=0;i)
{
if(i+nxts[i]==l2) ans=max(ans,nxts[i]);
//当文本串后缀起始的位置加上模式串匹配的nxts数组的值==文本串的长度,说明匹配。
}
if(ans>0)
{
for(i=0;i"%c",ch1[i]);
printf(" %d\n",ans);
}
else printf("0\n");
}
return 0;
}
//思路一把两个串合在一起的做法。
#include
#include<string.h>
#include
#include
#include
using namespace std;
const int N=5e4+10;
int net[N*2],len;
char a[N],b[N*2];
void getnext()
{
net[0]=-1;
int k=-1,j=0;
while(j<len)
{
if(k==-1||b[j]==b[k])
net[++j]=++k;
else k=net[k];
}
}
int main()
{
while(~scanf("%s%s",b,a))
{
int m=strlen(a),n=strlen(b);
strcat(b,a);
len=strlen(b);
getnext();
if(net[len]*2>len)
{
int t=min(m,n);
for(int i=0;i)
printf("%c",b[i]);
printf(" %d\n",t);
}
else if(net[len]==0) printf("0\n");
else
{
for(int i=0;i)
printf("%c",b[i]);
printf(" %d\n",net[len]);
}
}
}
hdu(1686)——Oulipo
题意:
给你两个串A,B,让你求A串在B串中的出现次数。
思路:
这个题目有两种做法,第一种就是用扩展kmp做,模板题,在这里我就不讲这种做法了,太简单了!
第二种做法就是用kmp做,利用kmp算法中的next数组来解,我们多求一位next数组,即整个字符串的最长公共前后缀,然后我们进行kmp匹配时,当模式串匹配完成一次后,我们让模式串的那个指针等于多求的那位next数组,继续匹配。具体看代码
//这道题的next数组是由优化的next数组写的,next数组的含义不是指最长前缀与后缀的长度,因为加了递归优化,值一般就是-1与0.
//求最开始的匹配位置。(题目意思)
#include
#include
#include
using namespace std;
const int maxm = 1e6 + 5;
int nex[maxm];
int t;
char a[maxm], b[maxm];
void getnext() {
nex[0] = -1;
int k = -1;
int j = 0 ;
int m = strlen(b);
while(j < m) {
if(k == -1 || b[j] == b[k]) {
j++, k++;
if(b[j] != b[k]) nex[j] = k;
else nex[j] = nex[k];
}
else k = nex[k];
}
}
int kmpc() {
int i = 0, j = 0;
int len1 = strlen(a);
int len2 = strlen(b);
int cnt = 0 ;
getnext();
while(i < len1) {
if(j == -1 || b[j] == a[i]) {
i++, j++;
}
else j = nex[j];
if(j == len2) cnt++;
}
return cnt;
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%s%s", b, a);
memset(nex, 0, sizeof(nex));
printf("%d\n", kmpc());
}
return 0;
}
例题四:
【POJ2752】
题目链接:http://poj.org/problem?id=2752
题意:
给你一个字符串,让你找出这个字符串中所有即是前缀又是后缀的字串的长度
很显然,这个字符串本身就是我们要找的字符串
我们很快可以发现,我们需要找的字符串一定是该字符串的相同的最长前缀和最长后缀的字串
比如说
ababcababababcabab
满足条件的子串有 ababcabab abab ab
思路:
这就是kmp中next数组的应用,next数组求得是最长的相等前后缀,而这个题目是要你求出所有的相等前后缀,所以我们按照kmp求next数组的方法,当求出next数组后,我们再对字符串最后的位置(str.size() 位置)求一遍next数组,只不过不同的是,当我们验证相等后,不是立即结束,而是寻找其它的更短的满足条件的相等前后缀。
#include
#include
#include
using namespace std;
int Next[400005];
char str[400005];
int ans[400005];
int cnt;
int len;
void getNext()
{
Next[0] = -1;
int i = 0, j = -1;
while (i < len)
{
if (j == -1 || str[i] == str[j])
{
++i;
++j;
Next[i] = j;
}
else j = Next[j];
}
}
int main()
{
while(scanf("%s", str)!= EOF) {
len = strlen(str);
getNext();
cnt = 0;
// printf("%d\n", Next[len]);
int t = Next[len];
while(t != 0) {
if(str[len - 1] == str[t - 1]) ans[cnt++] = t;
t = Next[t];
}
for(int i = cnt - 1; i >= 0; i--) {
printf("%d ", ans[i]);
}
printf("%d\n", len);
}
return 0;
}
https://blog.csdn.net/dyx404514/article/details/41831947 扩展kmp算法
https://vjudge.net/problem/HDU-4333 题目链接
题意:给你一个数,每次把这个数尾巴上的一个数字放到前面来,问如此循环一遍形成的新的(不重复)数字中,大于,等于,小于原数字的数各有多少个。
比如样例:341->134->413->341,小于、等于、大于的各有1个。
这个串后面接上它本身,作为主串,原串作为模式串。显然这题就是要求出主串每个后缀与模式串的最长公共前缀,直接套扩展KMP模板即可。
因为形成的新的数字必须不重复,因此还需要用KMP的next函数求一下最短循环节。
题解:将两个字符串拼接在一起,然后求出该字符串的循环节。(画一下图九能够看出),如果该点的extends值大于这个长度的话,说明这个值和原始字符串一样的,然后其他的就只要比较I + extend[i]的值,因为其他的相同,而且那个点的值也一定是不同的,要不然extend值更大。
#include
#include
#include
#include
#include
#include
#include
Clairewd’s message
HDU - 4300
这一题的意思是要传一段字符串,输入第一行是26个字母对应的字母,第二行是要传字符串,本来这个字符串是前面是加密的,后面是没有加密的,但是现在只截取了前面一部分(保证前面加密的是完整的)
要你输出本来要传的字符串。
#include
#include
#include
#include
#include
#include
#include
H - Messenger
CodeForces - 631D
https://vjudge.net/contest/300092#problem/H
kmp的一道变形题。
#include
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
struct node{
ll len;
char ch;
}p;
int nxt[maxn];
bool ok(node a, node b){
if(a.len==b.len&&a.ch==b.ch) return true;
return false;
}
void cal_next(node *r, int len){
nxt[0]=-1;
int j=0, k=-1;
while(j<len){
if(k==-1||ok(r[k], r[j])) j++, k++, nxt[j]=k;
else k=nxt[k];
}
}
bool judge(node a, node b){
if(a.ch==b.ch&&a.len>=b.len) return true;
return false;
}
ll KMP(node *s, int n, node *t, int m, node a, node b){
cal_next(t, m);
int i = 1, j=0;
ll cnt=0;
// for(i=1; i//从第二块开始匹配;
// while(j&&!ok(s[i], t[j])) j=nxt[j];
// if(ok(s[i], t[j])) j++;
// if(j==m){
// if(judge(s[i+1], b)&&judge(s[i-m], a)) cnt++;//中间块匹配成功后,判断首尾;
// j=nxt[j];
// }
// }
// for(int i = 0; i <= m; i++) {
// printf("%d\n", nxt[i]);
// }
// printf("%d\n", m);
while(i < n - 1) {
if(j == -1 || ok(s[i], t[j])) i++, j++;
else j = nxt[j];
if(j == m) {
// printf("%d %d\n", i, j);
if(i - m - 1 >= 0 && i <= n - 1 && judge(s[i], b) && judge(s[i - m - 1], a)) {
cnt++;
// j = nxt[j];
}
}
}
return cnt;
}
ll ans1(node *s, int len, node a){//特判只有一块;
ll cnt=0;
for(int i=0; i){
if(judge(s[i], a)){
cnt+=(s[i].len-a.len+1LL);
}
}
return cnt;
}
ll ans2(node *s, int len, node a, node b){//特判只有两块;
ll cnt=0;
for(int i=0; i1; i++){
if(judge(s[i], a)&&judge(s[i+1], b)) cnt++;
}
return cnt;
}
int len_s, len_t;
node s[maxn], t[maxn], q[maxn];
int main(){
int n, m;
while(~scanf("%d%d", &n, &m)){
len_s=len_t=0;
for(int i=0; i){
ll l;
char r[5];
scanf("%lld%s", &l, r);
p.len=l;
p.ch=r[1];
if(len_t==0) t[len_t++]=p;
else{
if(p.ch==t[len_t-1].ch){
t[len_t-1].len+=p.len;
}
else t[len_t++]=p;
}
}
for(int i=0; i){
ll l;
char r[5];
scanf("%d%s", &l, r);
p.len=l;
p.ch=r[1];
if(len_s==0) s[len_s++]=p;
else{
if(p.ch==s[len_s-1].ch){
s[len_s-1].len+=p.len;
}
else s[len_s++]=p;
}
}
node a, b;
a=s[0], b=s[len_s-1];//令a, b分别为首尾块;
int len_q=0;
for(int i=1; i1; i++){
q[len_q++]=s[i];//把p初始化为中间块;
}
if(len_s>2)
printf("%lld\n", KMP(t, len_t, q, len_q, a, b));
else{
if(len_s==1) printf("%lld\n", ans1(t, len_t, a));
else{
printf("%lld\n", ans2(t, len_t, a, b));
}
}
}
return 0;
}