KMP 模板 POJ 2406 HDU 3746

先上两大神的文章帮助大家理解KMP

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

https://www.cnblogs.com/yjiyjige/p/3263858.html

https://blog.csdn.net/v_july_v/article/details/7041827
 

之后就直接上板子了
 

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<algorithm> 
using namespace std;
char s1[100005];
char s2[100005];
int next[100005];
int n,len1,len2;

void  getnext() {//基础版next数组 
	int j=0,k=-1;
	next[0]=-1;
	while(j<len2){
		if(k==-1||s2[j]==s2[k])
		{
			next[++j]=++k;
		}
		else
		k=next[k];
	}
}
void getnext(){//优化版 next数组 
	int j=0,k=-1;
	next[0]=-1;
	while (j<len2){
		if(j==-1||s2[j]==s2[k])
		{
			++j;
			++k;	
			if(s2[j]!=s2[k])
				next[j]=k;
			else
				next[j]=next[j];
		}
		else
		k=next[k];
	}
}

int kmp_index(){//统计第一次匹配出现的位置 
	int i=0;//主串的位置 
	int j=0;//模式串的位置 
	getnext();
	while(i<len1 && j<len2)
	{
		if(j==-1||s1[i]==s2[j])
		{
			i++;
			j++;
		}
		else if(j!=-1&&s1[i]!=s2[j])
		j=next[j];
	}
	if(j==len2)
	return i-j;
	else
	return -1;
}
int kmp_count(){//统计匹配字符串出现的个数,可以重叠 
	int ans=0,i=0,j=0;
	while(i<len1)
	{
		if(j==-1||s1[i]==s2[j])
		{
			i++;
			j++;
		}	
		else
		j=next[j];
		if(j==len2)
		{
			ans++;
			j=next[j];
		}
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	while(n--){
		scanf("%s",s1);
		scanf("%s",s2);
		len2=strlen(s2);
		len1=strlen(s1);
		getnext();
		//for(int i=0;i<=len2;i++)
		//printf("%d ",next[i]);
		int ans_index=kmp_index();
		int ans_count=kmp_count();
		printf("%d\n",ans_index);
		printf("%d\n",ans_count);
	}
	return 0;
}

 

HDU 3746   next数组性质

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3746

题目大意:

给你一个字符串,要求将字符串的全部字符最少循环2次需要添加的字符数。

例子:

abcabc 已经循环2次,添加数为0

abcac 没有循环2次,添加字符abcac。数目为5.

abcabcab 已经循环过2次,但第三次不完整,需要添加数为1

我目前只能看懂未优化版的kmp每个字符的对应的next数组的值 就是 最大前缀等于最大后缀的长度 

上代码,在代码中体会奥义

自己在纸上画一画,体会一下next数组的意义和性质

#include<iostream>
#include<string.h>
using namespace std;
int next[1000005];
char a[100002];
int len;
void getnext()
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<len)
    {
        if(j==-1 || a[i]==a[j])
        next[++i]=++j;
        else j=next[j];
    }
}
int main()
{
    int n;
    
    scanf("%d",&n);
    while(n--)
    {
        
        scanf("%s",a);
        len=strlen(a);
        getnext();
 		int lent=len-next[len];//循环节的长度 
 		if(lent!=len&&len%lent==0)//如果循环节的长度不等于字符串长度,并且字符串长度%循环节长度为0 
 		{
 			cout<<"0"<<endl;
		}
		else
		cout<<lent-next[len]%lent<<endl;
    }
    
}

POJ 2406 

题目连接:http://poj.org/problem?id=2406

这题也运用到了next数组的性质   循环节的长度。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 1000005;

int next[maxn];

void get(char *s) {
    int l = strlen(s);
    int j = 0, k = -1;
    next[0] = -1;
    while(j < l) {
        if(k == -1 || s[j] == s[k]) {
            next[++j] = ++k;
        } else {
            k = next[k];
        }
    }
}
char s[maxn];

int main() {
    while(gets(s) ) {
        if(strcmp(s,".") == 0) {
            break;
        }
        get(s);
        int ans = 1;
        int l = strlen(s);
        if(l % (l - next[l]) == 0) {
            ans = l / (l - next[l]);
        }
        printf("%d\n", ans);
    }
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值