HDU 6863 Isomorphic Strings 字符串哈希

题目链接

题意:

给定一个字符串 s s s,长度是 n n n,问是否存在一个整数 k k k,满足 k ∣ n k|n kn,且将 s s s分成相等长度的 k k k段子串,每一段子串都是循环同构,两个串 s 1 s1 s1 s 2 s2 s2循环同构就是满足存在一个 i i i,使得

s 2 = s 1 i + 1 s 1 i + 2 … s 1 1 s 1 2 … s 1 i s2=s1_{i+1}s1_{i+2}\dots s1_1s1_2\dots s1_i s2=s1i+1s1i+2s11s12s1i

题解:

我们可以枚举所有 n n n的因数 k k k,对于每个因数 k k k,计算每个子串的哈希值集合 a a a,同时求出第一段子串的所有循环同构的哈希值集合 b b b,如果 a a a b b b的子集,那么该因数 k k k合法。

在判断时,我们可以使用一个哈希数组来记录,同时用一个变量标记,不能每次重新赋值标记变量值,否则会 w a wa wa

实现细节见代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int MAXN = 1e7 + 10;
const int mod = 1e7 + 7;
const int base = 1331; // 哈希基数
int hash1[MAXN], n, hs[MAXN];
char s[MAXN];
int hs1[mod], cnt;
int geth(int l, int r) { // 获取子串的哈希值
	return (hs[r] - hs[l - 1] * hash1[r - l + 1] % mod + mod) % mod;
}
int solve(int k, int id) {
	if (k == 1) return 0;
	int len = n / k;
	int l = 1, r = len;
	hs1[geth(l, r)] = id;
	for (int i = 1; i < len; i++) {  // 计算循环同构子串的哈希值
		int now = (geth(i + 1, len) * hash1[i] % mod + geth(1, i)) % mod;
		hs1[now] = id;
	}
	for (int i = 1; i <= k; i++) {
		int now = geth(l, r);
		if (hs1[now] != id) return 0; // 如果该子串属于同构字符串集合
		l += len, r += len;
	}
	return 1;
}
signed main() 
{
	int t;
	hash1[0] = 1;
	for (int i = 1; i < 5e6 + 5; i++) {
		hash1[i] = (hash1[i - 1] * base) % mod;
	}
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        scanf("%s", s + 1);
        for (int i = 1; i <= n; i++) {
        	hs[i] = (hs[i - 1] * base % mod + (s[i] - 'a' + 1)) % mod; // 计算原串哈希值
        }
        bool flag = false;
        for (int i = 1; i * i <= n; i++) {
        	if (n % i == 0) {
        		if (solve(i, ++cnt)) {
        			flag = true;
        		}
        		if (solve(n / i, ++cnt)) {
        			flag = true;
        		}
        	}
        	if (flag) {
        		break;
        	}
        }
        if (flag) {
        	puts("Yes");
        }
        else {
        	puts("No");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值