校内学习——串(kmp算法

熟记于心的模板: 

这里的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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值