一、 题目: Power Strings
二、 题意: http://acm.pku.edu.cn/JudgeOnline/problem?id=2406
三、 解决办法:kmp
首先要理解kmp的基本原理!对于kmp算法,由于我的数据结构的老师没有讲述到,就只好我自己去看书了,然而我本人智商有限,看这哪几页,也看不懂到底再说什么。简简单单的几行代码却让人想得头都晕!终于看到网上有关kmp算法相解决,再结合数据结构的介绍,才勉强明白了!那么,解这道题就简单的多了!
这里我就自己讲讲kmp算法,看看自己是否真正对kmp算法弄明白!
Kmp算法是模式匹配的一种改进算法!
如果按照传统的算法:
//参考《数据结构》中的程序
int Index(String S,String T,int pos)
{
i = pos; j = 1; //这里的串的第1个元素下标是1
while (i <= S.Length && j <= T.Length)
{
if (S[i] == T[j]) {++i; ++j;}
else {i = i – j + 2; j = 1;}
}
if (j > T.Length) return I - T.Length;//匹配成功
else return 0;
}
仍然用《数据结构》所讲述的例子分析:A=ababcabcacbab,B=abcac
在第一趟匹配:i = 3; j = 3;
在第二趟匹配:i= 2; j = 2;
我仅仅说到这里,i其实在反复回溯,又j也在回溯,这样的话,时间复杂度就大很多了!
而kmp算法的改进就在于:每当一趟匹配过程中出现字符不相等的情况下,不需要回溯指针i。从而将时间复杂度大大降低!
//参考《数据结构》中的程序
int Index_KMP(SString S, SString T, int pos)
{
//利用模式串T的next函数求T在主串S中第pos个字符之后的位置
//的kmp算法。其中,T非空,1<=pos<=StrLength(S)。
i = pos; j = 1;
while (i <= S[0] && j <= T[0])
{
//继续比较后续字符
if (j == 0 || S[i] == T[j])
{
++i;
++j;
}
//模式串向右移动
else
{
j = next[j];
}
}
if (j > T[0])
{
return i – T[0];
}
else
{
return 0;
}
}//
下面还是以教材中的例子进行说明:
期中需要说明的是:我们预处理出这样一个数组next[j],表示当匹配到T数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。next[j]应该是所有满足T[1..next[j]]=S[j-next[j]+1..j]的最大值。
S=acaaabaabcacaabc
T=abcac
刚开始: i = 1; j = 1;
执行:if (j == 0 || S[i] == T[j])
{
++i;
++j;
}
则凡是主串与模式串可以匹配的话,均++i,++j;但遇到S[i] != T[j],模式串向右移动, j = next[j];则不必要考虑指针i的问题啦!节省了时间!但现在问题是:next[j]如何计算出来?
由next[j]的定义可知,j的值与S无关!而当遇到主串与模式串不能匹配的时候,就必须考虑到j的值应为多少,即在之前匹配成功多少个?所以我们可以在匹配成功的情况下同时记录next[i]=j,一旦遇到匹配不成功时,则将j=next[j],这样就如课本所说,做到滑过去第i个匹配的字符!实质就是模式串的自我匹配!
程序:
i = 1;j = 0;next[0] = -1;
while (i < T[0])
{
if (j == 0 || T[i] == T[j])
{
if (T[i] != T[j])
{
++i; ++j;next[i] = j;
}
else
{
j = next[j];
}
}
}
再一次改进:
i = 1;j = 0;next[0] = -1;
while (i < T[0])
{
if (j == 0 || T[i] == T[j])
{
++i; ++j;
if (T[i] != T[j])
{
next[i] = j;
}
else
{
next[i] = next[j];
}
else
{
j = next[j];
}
}//
现在回到题目:Power Strings。由题意可知就是模式串的自我匹配!问题就变得简单了!
四、 源代码:
#include "stdio.h"
#include "string.h"
#define MAX 1000000
char s[MAX];
int next[MAX];
int main()
{
freopen("1.txt","r",stdin);
int i, j, len;
while (scanf("%s", s) != NULL)
{
if (s[0] == '.' && s[1] == '/0')
{
break;
}
len = strlen(s);
i = 0;
j = -1;
next[0] = -1;
while (i < len)
{
if (j == -1 || s[i] == s[j])
{
++i;
++j;
if (s[i] != s[j])
{
next[i] = j;
}
else
{
next[i] = next[j];
}
}
else
{
j = next[j];
}
}
i -= j;
if (len % i == 0)
{
i = len / i;
}
else
{
i = 1;
}
printf("%d/n", i);
}
return 0;
}
五、Memory: 3108K Time: 110MS
六、总结:
在平时的学习过程中,就应该培养对每一个知识点搞明白!反正现在是读大学了,已经远离应试教育的折磨了;学东西就要将自己感兴趣的学好!