POJ 2406 Power Strings--字符串哈希(bkdhash)-KMP(next)

如果暴力解决的话,时间复杂度应为:O(n^2).

采用字符串哈希,时间复杂度为:O(n*lgn).

采用KMP算法的next数组,时间复杂度为:O(n).


我用字符串哈希(bkdhash)写的,虽然不及kmp,但还是粘上吧:

#include
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)

POJ 2406 Power Strings--字符串哈希(bkdhash)-KMP(next)_第1张图片

写的我自己都看不懂了...

代码如下:

#include
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;
	}
}


你可能感兴趣的:(字符串处理,哈希hash)