`
isiqi
  • 浏览: 16160921 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

KMP算法模板及各种应用

 
阅读更多

http://poj.org/problem?id=2406

给定一个字符串,问最多是多少个相同子串不重叠连接构成。

KMP的next数组应用。这里主要是如何判断是否有这样的子串,和子串的个数。

若为abababa,显然除其本身外,没有子串满足条件。而分析其next数组,next[7] = 5,next[5] = 3,next[3] = 1,即str[2..7]可由ba子串连接构成,那怎么否定这样的情况呢?很简单,若该子串满足条件,则len%sublen必为0。sunlen可由上面的分析得到为len-next[len]。

因为子串是首尾相接,len/sublen即为substr的个数。

若L%(L-next[L])==0,n = L/(L-next[L]),else n = 1

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

char pattern[1000002];
int next[1000002];

/*
kmp算法,需要首先求出模式串的next函数值
next[j] = k,说明 p0pk-1 == pj-kpj-1,也就是说k为其前面相等串的长度
*/
void get_nextval(const char* pattern)
{
	int i=0,j=-1;
	next[0]= -1;
	while(pattern[i] != '\0')
	{
		if(j== -1 || pattern[i]== pattern[j] )     //pattern[i]表示后缀的单个字符,pattern[j]表示前缀的单个字符
		{
			++i;
			++j;
			if(pattern[i] != pattern[j])
				next[i]=j;
			else
				next[i]=next[j];
		}
		else
			j=next[j];    //若j值不相同,则j值回溯
	}
}//get_nextval

int main(void)
{
	int len;
	while(scanf("%s",pattern)!=EOF)
	{
		if(pattern[0]=='.')
			break;
		len=strlen(pattern);

		get_nextval(pattern);			
		
		if(len%(len-next[len])==0)
			printf("%d\n",len/(len-next[len]));
		else
			printf("1\n");
			
	}
	return 0;
}

http://poj.org/problem?id=1961

大意:
定义字符串A,若A最多由n个相同字串s连接而成,则A=s^n,如"aaa" = "a"^3,"abab" = "ab"^2
"ababa" = "ababa"^1

给出一个字符串A,求该字符串的所有前缀中有多少个前缀SA= s^n(n>1)
输出符合条件的前缀长度及其对应的n

如aaa
前缀aa的长度为2,由2个'a'组成
前缀aaa的长度为3,由3个"a"组成

分析:KMP
若某一长度L的前缀符合上诉条件,则
1.next[L]!=0(next[L]=0时字串为原串,不符合条件)
2.L%(L-next[L])==0(此时字串的长度为L/next[L])

对于2:有str[0]....str[next[L]-1]=str[L-next[L]-1]...str[L-1]
=》str[L-next[L]-1] = str[L-next[L]-1+L-next[L]-1] = str[2*(L-next[L]-1)];
假设S = L-next[L]-1;则有str[0]=str[s]=str[2*s]=str[3*s]...str[k*s],对于所有i%s==0,均有s[i]=s[0]
同理,str[1]=str[s+1]=str[2*s+1]....
str[j]=str[s+j]=str[2*s+j]....
综上,若L%S==0,则可得L为str[0]...str[s-1]的相同字串组成,
总长度为L,其中字串长度SL = s-0+1=L-next[L],循环次数为L/SL
故对于所有大于1的前缀,只要其符合上述条件,即为答案之一

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

char pattern[1000002];
int next[1000002];

/*
kmp算法,需要首先求出模式串的next函数值
next[j] = k,说明 p0pk-1 == pj-kpj-1,也就是说k为其前面相等串的长度
*/
void get_nextval(const char* pattern)
{
	int i=0,j=-1;
	next[0]= -1;
	while(pattern[i] != '\0')
	{
		if(j== -1 || pattern[i]== pattern[j] )
		{
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];
	}
}//get_nextval

int main(void)
{
	int i,len,n,j=1;
	while(scanf("%d",&n)!=EOF)
	{
		if(!n)
			break;
		scanf("%s",pattern);
		len=strlen(pattern);

		get_nextval(pattern);
	
		printf("Test case #%d\n",j++);
		for(i=2;i<=len;i++)
		{
			if(i%(i-next[i])==0 && i/(i-next[i])>1)
				printf("%d %d\n",i,i/(i-next[i]));
		}
		printf("\n");
			
	}
	return 0;
}

http://poj.org/problem?id=2752
大意:
给出一个字符串A,求A有多少个前缀同时也是后缀,从小到大输出这些前缀的长度。

分析:KMP
对于长度为len的字符串,由next的定义知:
A[0]A[1]...A[next[len]-1]=A[len-next[len]]...A[len-1]此时A[0]A[1]...A[next[len]-1]为一个符合条件的前缀
有A[0]A[1]....A[next[next[len]]-1] = A[len-next[next[len] - next[next[len]]]...A[next[len]-1],故A[0]A[1]....A[next[next[len]]-1]也是一个符合条件的前缀
故从len=>next[len]=>next[next[len]] ....=>直到某个next[]为0均为合法答案,注意当首位单词相同时,也为答案。

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

char pattern[400002];
int next[400002];

/*
kmp算法,需要首先求出模式串的next函数值
next[j] = k,说明 p0pk-1 == pj-kpj-1,也就是说k为其前面相等串的长度
*/
void get_nextval(const char* pattern)
{
	int i=0,j=-1;
	next[0]= -1;
	while(pattern[i] != '\0')
	{
		if(j== -1 || pattern[i]== pattern[j] )
		{
			++i;
			++j;
			next[i]=j;
		}
		else
			j=next[j];
	}
}//get_nextval

int main(void)
{
	int i,len,n;
	vector<int>ans;
	while(scanf("%s",pattern)!=EOF)
	{
		ans.clear();
		len=strlen(pattern);
		get_nextval(pattern);
		n=len;
		while(n)
		{
			ans.push_back(n);
			n=next[n];
		}
		if(pattern[0]==pattern[n-1])   //首部、尾部字符相同
			ans.push_back(1);

		i=ans.size()-1;
		for(;i>0;i--)
			printf("%d ",ans[i]);
		printf("%d\n",ans[0]);
	}
	return 0;
}




分享到:
评论

相关推荐

    kmp算法模板与应用.txt

    kmp算法模板与应用.txt

    C++ kmp算法模板代码解读

    C++编程语言虽然功能强大,应用方式灵活,但是在实际编程中同样会出现各种各样的错误。在这里我们将会为大家详细介绍一下有关C++指针漂移的解决方法,希望本文介绍的内容可以帮助大家解决问题。

    KMP 算法实例详解

    KMP算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,其对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,是一个非常优秀的模式匹配算法。 分析:KMP模板题、KMP的关键是求出next的...

    数据结构算法

    树状数组 经典算法题每日演练——第九题 优先队列 经典算法题每日演练——第八题 AC自动机 经典算法题每日演练——第七题 KMP算法 经典算法题每日演练——第六题 协同推荐SlopeOne 算法 经典算法题每日演练——第五...

    ACM算法模版大集合

    路径压缩思想的应用 STL中的数据结构 vector deque set / map 动态规划 / 记忆化搜索 动态规划和记忆化搜索在思考方式上的区别 最长子序列系列问题 最长不下降子序列 最长公共子序列 一类NP问题的...

    ACM算法模板集锦(几何,结构,其他,数论,数值计算,图论)

    树的优化算法 拓扑排序(邻接阵形式) 最佳边割集 最佳顶点割集 最小边割集 最小顶点割集 最小路径覆盖 图论_最短路径\ 最短路径(单源bellman_ford邻接阵形式) 最短路径(单源dijkstra邻接阵形式) 最短路径...

    扩展KMP KMP

    KMP:给出两个字符串A(称为模板串)和B(称为子串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0),求出A[i]往前和B的前缀匹配的最大匹配长度,记为ex[i](或者说,ex[i]为满足A[i-z+1..i]==B[0..z-...

    ACM模板和一些题目的代码实现

    代码可能包括字符串匹配(KMP/Boyer-Moore)、编辑距离、后缀数组等算法。 数据结构:组织和存储数据的方式,如数组、链表、栈、队列、树、图等。代码实现这些结构的基本操作,以支持高效的数据处理。 数论:研究...

    浙江大学ACM模板 计算几何,图论,数据结构,经典题的模板

    5.1 KMP应用 130 5.2 后缀数组 131 5.3 中缀表达式转后缀表达式 134 5.4 Firefighters 表达式求值 135 6.博弈 139 6.1 博弈的AB剪枝 139 6.1.1 取石子 139 6.2 博弈 SG函数 局势分割 141 7.数据结构 142 7.1 TRIE ...

    ACM常用模板总结ACM常用模板总结

    树的优化算法 拓扑排序(邻接阵形式) 最佳边割集 最佳顶点割集 最小边割集 最小顶点割集 最小路径覆盖 图论_最短路径\ 最短路径(单源bellman_ford邻接阵形式) 最短路径(单源dijkstra邻接阵形式) 最短路径...

    浙江大学ACM模板(经典代码)

    10.3 树的优化算法 94 10.4 拓扑排序(邻接阵) 95 10.5 最佳边割集 96 10.6 最佳点割集 97 10.7 最小边割集 98 10.8 最小点割集 99 10.9 最小路径覆盖 101 11、 图论—支撑树 101 11.1 最小生成树(kruskal邻接表) ...

    【白雪红叶】JAVA学习技术栈梳理思维导图.xmind

    KMP算法 GZZ算法 HASH分桶 关联规则算法 APRORIVE算法 分布式 负载均衡 水平伸缩 集群 分片 Key-hash 异步 一致性hash 消峰 分库分表 锁 悲观锁 乐观锁 行级锁 分布式锁 分区排队 一致性 ...

    百度校园招聘移动软件开发工程师笔试题目.doc

    KMP算法是基于字符串的Hash值来查找子串的位置,而Rabin-Karp算法是基于字符串的Hash值和模运算来查找子串的位置。 2. 请使用非递归方式实现二叉树的后序遍历。 二叉树是一种树形数据结构,后序遍历是指从左到右、...

    ACM经典、常用代码

    这是我整理过的关于ACM题目常用到的算法代码,word文档,条理清晰,绝对有用。目录如下: 一.数论 1.阶乘最后非零位 2. 模线性方程(组) 3. 素数表 4. 素数随机判定(miller_rabin) 5. 质因数分解 6. 最大公...

    数据结构(C++)有关练习题

    内容及步骤: 1、 设计一个图的类,采用临接表法进行存储,该图每个结点的数据类型类模板的模板参数进行定义(注:需先设计一个结点类Node); 2、 为该类分别设计一个实现深度优先搜索和广度优先搜索的成员...

Global site tag (gtag.js) - Google Analytics