如果暴力解决的话,时间复杂度应为:O(n^2).
采用字符串哈希,时间复杂度为:O(n*lgn).
采用KMP算法的next数组,时间复杂度为:O(n).
我用字符串哈希(bkdhash)写的,虽然不及kmp,但还是粘上吧:
#include<iostream>
using namespace std;
typedef unsigned long long ull;
char arr[1000001];
ull nbase[1000001];
ull Hash[1000001];
int base = 31;
void main()
{
nbase[0] = 1;
for (int i = 1; i < 1000001; ++i)
nbase[i] = nbase[i - 1] * base;
while (scanf_s("%s", arr, 1000001))
{
int len = strlen(arr);
ull n = 0;
Hash[len] = 0;
for (int i = len - 1; i >= 0; --i)
Hash[i] = Hash[i + 1] * base + arr[i] - 'a' + 1;
for (int k = 1; k <= len; ++k)
{
if (len%k != 0) continue;
ull temp = Hash[0] - Hash[k] * nbase[k];
int j = 0;
for (j = k; j < len; j = j + k)
{
if (temp != Hash[j] - Hash[j + k] * nbase[k]) break;
else temp = Hash[j] - Hash[j + k] * nbase[k];
}
if (j == len)
{
n = len / k;
break;
}
}
cout << n;
}
}
关于bkdhash:http://blog.csdn.net/wanglx_/article/details/40400693
我自己的直观的理解:
为了方便说明,
假设:base = 3,数的范围为0-63 (代码中实际情况是,base=31,数据类型为unsigned long long)
对于字符串abcabc:
index: 0 1 2 3 4 5
arr: a b c a b c
hash: 56 61 41 34 11 3 (计算公式:hash[i]=hash[i+1]*base+arr[i]-'a'+1) 这里面有个问题是:因为数的范围为0-63 ,最大为63,可能存在数的溢出,比如:
从右往左,a->c这一步,hash[2] = hash[3]*3+'c'-'a'+1 =34*3+2+1= 105。105大于63,这么办?
在计算机中会自动取模(也就是取余数):105% 64=41。其他hash值都是这么计算的,每步都会取余数的。
同样, nbase的计算也是如此:3 9 27 17(81%64) ......
那么计算机怎么判断前三个字符("abc")与后三个字符("abc")相等呢?取模后计算会不会出现负值?
后三个字符:temp0 = hash[3]-hash[6]*nbase[3] = 34 - 0*27 = 34
前三个字符:temp1 = hash[0]-hash[3]*nbase[3] = 56 -34*27 = ? 。这里34*27实际是(34*27)%64=22,所以56-22=34。
所以temp0=temp1。
关于使用KMP算法的next数组,我是这么想的:
(kmp:http://blog.csdn.net/v_july_v/article/details/7041827#comments)
写的我自己都看不懂了...
代码如下:
#include<iostream>
using namespace std;
int getNext(char* arr, int len) //返回最后一个值:next[len-1]
{
int* next = new int[len];
memset(next, 0, len*sizeof(int));
for (int i = 1; i < len; ++i)
{
int p = next[i - 1];
while (p > 0 && arr[i] != arr[p])
p = next[p - 1];
if (p == 0 && arr[i] != arr[p]) //当p == 0 && arr[i] != arr[p]时,单独考虑:next[i] = 0。
next[i] = 0;
else next[i] = p + 1;
}
return next[len - 1];
}
void main()
{
char arr[1000001] = { '\0' };
while (scanf_s("%s", arr, 1000001))
{
int len = strlen(arr);
int next = getNext(arr, len);
if (len % (len - next) == 0)
cout << len / (len - next) << endl;
else
cout << 1 << endl;
}
}