熟记于心的模板:
这里的next和书本上不太一样,next[i]=k 代表——在前i个字符的字串中,前k个和后k个相同
书本上: next[i]=k 代表——在前i-1个字符的字串中,前k-1个和后k-1个相同 {注意,在计算时不包括,前缀(为全部)=后缀(为全部)}
其实本质一样,差别只是在比较的时候书本是i和j对齐来看;
但是这里i总是在j后面一个,所以要用j+1和i比,但是在利用next移动的时候只移动j,这样,移动后包括j的前面对齐部分还是一样,j+1是未知的用来对比。
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = ne[j];
// 匹配成功后的逻辑
}
}
更符合个人习惯的模板:
//next数组
void next(char p[], int l, int ne[])
{
ne[1] = 0;
int j = 0; int i = 1;
while (i < l)
{
if (j == 0 || p[i] == p[j])ne[++i] = ++j;
else j = ne[j];
}
}
//匹配
int i = 1, j = 1;
while (j <= m && i <= n)
{
if (j == 0 || s[i] == p[j])
{
if (j == m)
{
cout << i - j << " ";
j = ne[j];
continue;
}
i++; j++;
}
else
j = ne[j];
}
#include<iostream>
using namespace std;
const int N=100010,M=1000010;//很无奈必须开这么大
char q[N],s[M];
int ne[N];//保存next数组
int main()
{
int n,m;
cin>>n>>q+1>>m>>s+1;//下标均从1开始
for(int i=2,j=0;i<=n;i++)
//j表示匹配成功的长度,i表示q数组中的下标,因为q数组的下标是从1开始的,只有1个时,一定为0,所以i从2开始
{
while(j&&q[i]!=q[j+1]) j=ne[j];
//如果不行可以换到next数组
if(q[i]==q[j+1]) j++;
//成功了就加1
ne[i]=j;
//对应其下标
}
//j表示匹配成功的长度,因为刚开始还未开始匹配,所以长度为0
for(int i=1,j=0;i<=m;i++)
{
while(j&&s[i]!=q[j+1]) j=ne[j];
//如果匹配不成功,则换到j对应的next数组中的值
if(s[i]==q[j+1]) j++;
//匹配成功了,那么j就加1,继续后面的匹配
if(j==n)//如果长度等于n了,说明已经完全匹配上去了
{
printf("%d ",i-j);
//因为题目中的下标从0开始,所以i-j不用+1;
j=ne[j];
//为了观察其后续是否还能跟S数组后面的数配对成功
}
}
return 0;
}
(10.14) oj——字符串应用-实现KMP匹配算法
以下是我结合b站视频和ppt合并成的一个80分代码(还没找出问题):
solution:询问老师后,ababcabcacbab abcac 这个测试用例有问题
#include<iostream>
#include<cstring>
using namespace std;
void next(char p[], int l, int ne[])
{
ne[1] = 0;
int j = 0; int i = 1;
while (i <= l)
{
if (j == 0 || p[i] == p[j])ne[++i] = ++j;
else j = ne[j];
}
}
int main()
{
char*s=new char[3]; char* p = new char[3]; int * ne = new int[3];
cin >> s + 1 >> p + 1;
int n = strlen(s + 1); int m = strlen(p + 1);
next(p, m, ne);
int i = 1, j = 1;
while (j <= m && i <= n)
{
if (j == 0 || s[i] == p[j])
{
i++; j++;
}
else
j = ne[j];
if (j == m)
{
cout << i - j;
return 0;
}
}
cout << "no";
delete[] s;
delete[] p;
delete[] ne;
}
那个作者的代码改了一下,依旧是上面这个不过
#include<iostream>
#include<cstring>
using namespace std;
void next(char ch[], int ne[])
{
ne[1] = 0;
int i = 1, j = 0;
while (i < strlen(ch) - 1) {
if (j == 0 || ch[i] == ch[j]) ne[++i] = ch[i] == ch[j] ? ne[++j] : ++j;
else j = ne[j];
}
}
int main()
{
char* s = new char[3]; char* p = new char[3]; int* ne = new int[3];
cin >> s + 1 >> p + 1;
int n = strlen(s + 1); int m = strlen(p + 1);
s[0] = char(n); p[0] = char(m);
next(p,ne);
int i = 0;
int j = 1;
while (i < strlen(s) && j < strlen(p)) {
if (j == 0 || s[i]== p[j])
{
++i;
++j;
}
else {
j = ne[j];
}
}
if (j > strlen(p) - 1)
{
cout << i-j;
return 0;
}
else
{
cout << "no";
return 0;
}
}
最后终于在第一个版本的基础下debug成功!但是这个程序在vs运行仍然会报错,不知道为什么,但是结果都是正确的,刚刚的问题在于忽略了当i或者j到达最大值后,他们还会+1,与后面的程序对不上了!于是这里加了一个限制条件 ,终于成功
#include<iostream>
#include<cstring>
using namespace std;
void next(char p[], int l, int ne[])
{
ne[1] = 0;
int j = 0; int i = 1;
while (i < l)
{
if (j == 0 || p[i] == p[j])ne[++i] = ++j;
else j = ne[j];
}
}
int main()
{
char* s = new char[3]; char* p = new char[3]; int* ne = new int[3];
cin >> s + 1 >> p + 1;
int n = strlen(s + 1); int m = strlen(p + 1);
next(p, m, ne);
int i = 1, j = 1;
while (j <= m && i <= n)
{
if (j == 0 || s[i] == p[j])
{
if (j == m || i == n)
{
break;
}
i++; j++;
}
else
j = ne[j];
}
if (j == m)
{
cout << i - j;
return 0;
}
cout << "no";
delete[] s;
delete[] p;
delete[] ne;
}
第二块代码说明了一些限制范围问题,明天再研究一下,同时明天背一下另一种next数组的模板
(10.15/17)开始解决 oj—— 根据next数组推导模式字符串
这道题目是 -1,0开头的next数组,就用昨天0,1开头的next数组修改了一下,只修改了两行
重点:-1,0开头适用于数组从0开始,而0,1开头适用于数组从1开始。
而且 next[i]=k 代表——在前i-1个字符的字串中,前k个和后k个相同(不用-1了!!!)
void next(char p[], int l, int ne[])
{
ne[0] = -1;//只有这两排修改了!
int j = -1,i = 0;//这里修改:j从-1开始i从0开始
while (i < l)
{
if (j == -1 || p[i] == p[j])
ne[++i] = ++j;
else
j = ne[j];
}
}
10.17跟进
主体部分和满分几乎相同。多次调试,最后错误原因是 判断错误的if应该放在最后,我放在前面就错误了,还没有弄懂,不懂的部分是条件部分,晚上再来研究。
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int f, e, n;
cin >> f >> e >> n;
int* p = new int[n];
int* ne = new int[n];
p[0] = f; p[n - 1] = e;
int num;
for (int i = 0; i < n; i++)
{
cin >> num;
ne[i] = num;
}
for (int i = 2; i < n; i++)
{
if (ne[i] == 0)
{
p[i - 1] = (f == 1) ? 0 : 1;
}
else if(ne[i] - ne[i - 1] == 1)//前i个字符中,前ne[i]个和后ne[i]个相同
{
p[i - 1] = p[ne[i] - 1];
}
else if (ne[i] > i || (ne[i] - ne[i - 1] != 1 && p[i - ne[i] - 1] == p[0]) || ne[i] - ne[i - 1] > 1)
//把这个放在第一个就会错误
{
cout << "ERROR";
delete[]p;
delete[]ne;
return 0;
}
}
for (int i = 0; i < n; i++)
{
cout << p[i];
}
delete[]p;
delete[]ne;
return 0;
}