Cyclic Nacklace HDU - 3746(KMP next 数组应用)

有一个字符序列,现在问你:
序列后面最少补充几个元素使其恰能成为几个重复循环的序列。

Input

第一行是一个整数 T ( 0 < T <= 100 ) 代表测试数据的组数.
之后T行每行一个字符串. 由小写字母'a'-'z'组成. 字符串的长度为: ( 3 <= Len <= 100000 ).

Output

对于每组测试数据,输出一行,即序列后面最少补充几个元素使其恰能成为几个重复循环的序列。

输入样例

3
aaa
abca
abcde

输出样例

0
2
5

这道题目是让求一个字符串(周期相关之类的),就是说,当前字符串周期(也不能说是周期),就是在当前的状态,后面再来几个字符补全的话,是一个有周期的串(周期数要大于1的),也是跟next数组相关的知识有关。

一开始就认为将KMP 和next 的模板知道怎么求解就可以了,但是连续做到两个关于字符循环节的问题,我是彻底的蒙圈了。

我这里还是一些简单的知识,更详细的讲解是 July 的博客。我只是说一下这个  next 数组的应用。

这个next 数组下标是从1开始的(有些难受),这个next 数组里面的数据其实是当前位置(这里会有点疑惑,因为next 数组下标是从1开始的,而字符串的下标是从0开始的。现在有两种解决方法1.将字符串的下标看成是从1开始的,next 数据就是在当前(包括这个数据在内)的所有前后缀中,最长的相同前后缀数量;2.就是将字符串看为从0开始的,next 数组数据是除了当前下标,之前的最长的前后缀数量。)。然后再具体看这个next 数组:这个next 数组就是上面所说的前后缀数量最长的那个数量。

就是上面的这个图像,就可以清楚的看到next 数组的实质,这也为next 数组求解一些循环节和一些周期题目。例如 abcab 这个字符串 它的next 数组 就是0 0 0 1 2 ,我们通常用 len(字符串长度)-next[len] 来表示循环节的大小。就是这样,求解周期。这个是第一种应用;你可能会有一些疑惑,为什么是这个式子呢?这时就要不得不说这个next 数组的高明之处了,我们可以想一想,这个周期是怎么来的,例如我拿一个函数  f(x+0)=f(x+3)这个数学 函数的周期就是3,我感觉这个就跟这个next数组一模一样,只不过这个next数组是 以前缀和后缀  相同的最大数量表示(当前位置和之前的前缀和后缀相同数目,也就是这个函数 f(x) 能满足当前周期的最大的数量(自变量),我们就可以看出来当前位置之前和它组成的最大周期数,每个位置的可以看看这个位置之前的周期 ),这样周期和前后缀就组合在了一起。这时 你可能会想前面的字符串不会影响后面的吗?我的回答是永远不会 (这也可能是next 数组的高明之处,将每个状态之间都分得清清楚楚(怎么感觉像DP) ),我说个例子 abcabd 前面abcab 的next 数组是0 0 0 1 2,就看前面的周期数 是 abc  3,但是 加上 d,abcabd 这时next数组为 0 0 0 1 2 0,这里再套一下那个式子,整个字符串周期就是 6 ,真的是很牛逼,这个算法。这个每个状态之间都是独立的,恩,对,就是这样。这样来求周期再好不过了。

第二种应用:

就是上面的那道应用题目,就是让你加上一些字符,使这个字符串的循环计数(与周期数不一样,例如 abcabc 这个字符串,周期为3,而循环计数为2 ),首先得先求出来周期数,然后再去找那个需要加入的字符的数量。

我们来分析一下:1.首先这个字符串的循环计数要 大于1 ,如果前面没有出现循环计数大于1的情况,我们就需要加上字符;来使它循环计数数大于1。 例如 abcabcabc 已经出现循环计数大于1 的3了,我们就不再需要在加入字符了,而abca ,这个字符串我们可以认定为他的周期就是3,循环计数才1,我们得需要加入一些字符,使他的循环计数大于1,如果我们人类来想的话,很快说出答案,就是2,再加上bc 两个字符,就行了,就成为 abcabc 。

我们得分开判断只有一个字符的,和一些循环计数已经大于1的,比如 aaaa 结果是0,不用再加了;而a一个字符的,还得再加一个a才可以。

结果是0的好判断,就是下面这个 就像 aaaaa,这样的情况就是 输出0。

if(len!=length&&len%length==0) (length是周期,len是字符串长度)                    
	printf("0\n");

像其他的比如 abcab 型的,下面的情况解决,next[len]%length 是已经满足的数量,length-next[len]%length,就是再来几个字符的数量。

        else									 
	{  									
		int ans=length-anext[len]%length;   
		printf("%d\n",ans);	
	}

 就是这样,好像弄懂了一点点耶。

代码:
 

#include<iostream>
const int maxn=100005;
using namespace std;
#include<string>
#include<string.h>

int anext[maxn];

void getnext(string s)
{
	int i=0;
	int j=-1;
	
	anext[0]=-1;
	while(i<s.length())
	{
		if(j==-1||s[i]==s[j])
		{
			i++;
			j++;
			anext[i]=j;
		}
		else
			j=anext[j];
			
	}
	
}


int main()
{
	int n;
	scanf("%d",&n);
	
	char s[maxn]; 
	while(n--)
	{
		memset(anext,0,sizeof(anext));
		scanf("%s",s);
		getnext(s);
		
		
		int len=strlen(s);
		
		
		/*cout<<"next数组:"<<endl; 
		for(int i=1;i<=len;i++)
			cout<<anext[i]<<' ';
		cout<<endl;
		cout<<"周期:"<<len-anext[len];	
		cout<<endl;
		
		
		
		
		
		cout<<"要加的字符:";*/
		int length=len-anext[len];      
		
		
		//len 是字符串长度     
		
		if(len!=length&&len%length==0)                        
			printf("0\n");
		else									
		{  										
			int ans=length-anext[len]%length;   
			printf("%d\n",ans);	
		}
				
		
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/88357035