NSUOJ2877 KMP裸题(字符串哈希或kmp)

一、题目描述

Description
我们都知道哈希是一个很有趣的数据处理方式,尤其是水题的时候23333。
比如我们可以使用哈希加二分在nlogn的复杂度下解决O(n)的马拉车题目,
再比如我们可以在同样O(n)的复杂度下解决一部分KMP问题,
再比如我们可以在O(nlognlogn)的复杂度下解决O(nlogn)的后缀数组题目…
现在给你若干个字符串,需要你找到每个字符串最大的循环节。(KMP裸题+小声bb

Input
T组数据。(T<20)
每组一个字符串。长度小于1e6。
Output
对于每个字符串都在独立一行输出最大的循环节长度
Sample Input Copy
3
abcd
ababab
aaaaa
Sample Output Copy
1
3
5

一、题目思路

题目的意思给你一个字符串,让你找出它是由多少个相同的子串构成的,输出该子串的最大个数。

1、字符串哈希

处理串的哈希值遍历每次遍历跑就成,具体细节看代码,也就懂了。然后下面是一个单哈希的简单模板(蒟蒻)

#include <iostream>
#include <string>
using namespace std;
const int n = 1e5 + 7;
const int base = 133;        //p为将字符转化为数字的进制数,一般为质数
unsigned long long h[n];     //hash数组,记录值,使用unsigned long long 会自动溢出,取模
unsigned long long p[n];    //预处理p的n次方
char s[n];

int main()
{
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

	cin >> s + 1;         //从下标为1开始读
	int len = strlen(s + 1);
	h[0] = 0;
	for (int i = 1; i <= len; i++) {
		h[i] = (h[i - 1] * base) + s[i];
	}

	//abcdef
	//对应的映射值(哈希值)
	//a-228 ab-29966 abc-3925645 abcd-514259595 abcde-67368007046 abcdef-8825208923128
	for (int i = 1; i <= len; i++) {
		cout << h[i] << " ";
	}
	cout << endl;


	//求子串的hash值 [L,R] 闭区间 1 <= L <= R <= N
	//hash = hash[R] - hash[L - 1] *p[R - L + 1]

	return 0;
}
//不对的请斧正

2、kmp

这个博主写得超级好,可以看这边文章学习kmp
链接: kmp的详解.
使用next数组简单过

三、AC代码

1、哈希做法

#include <iostream>
#define uzl unsigned long long
using namespace std;
const int base = 133; 
const int M_X = 1e6 + 10;
uzl p[M_X], h[M_X];
char s[M_X];
int t, len;
uzl get_h(int L, int R) {                     //计算区间L到R的哈希值
	return h[R] - h[L - 1] * p[R - L + 1];
}
bool check(int x) {                           //计算X相邻串的哈希值
	for (int i = 2 * x; i <= len; i += x) {
		if (h[x] != get_h(i - x + 1, i))
			return false;
	}
	return true;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin >> t;
	while (t-- && cin >> s + 1) {          //从1开始读
		len = strlen(s + 1);
		
		h[0] = 0, p[0] = 1;                //预处理
		for (int i = 1; i <= len; i++) {
			h[i] = h[i - 1] * base + s[i]; //哈希函数
			
			p[i] = p[i - 1] * base;        //处理p的乘方,方便公式计算
		}
		for (int i = 1; i <= len; i++) {
			if (len % i == 0 && check(i)) {
				cout << len / i << endl;
				break;
			}
		}
	}
	return 0;
}

2、kmp做法

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int M_X = 1e6 + 7;
int t,len,ans, nnext[M_X];
char s[M_X];
void get_next()
{
    nnext[1] = 0;
    int j = 1, k = 0;
    while (j <= len) {
        if (k == 0 || s[j] == s[k]){
            k++;
            j++;
            nnext[j] = k;
        }else{
            k = nnext[k];
        }   
    }
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin >> t;
	while (t-- && cin >> s + 1) {
		len = strlen(s + 1);
		ans = 0;
        get_next();
        ans = len / (len + 1 - nnext[len + 1]);

        if (len % (len + 1 - nnext[len + 1]) == 0)
            cout << ans << endl;
        else
            cout << "1" << endl;
	}
    

	return 0;
}
上一篇:锋利的JQuery 学习笔记


下一篇:串和KMP算法