题意:
给定一个字符串 s s s,长度是 n n n,问是否存在一个整数 k k k,满足 k ∣ n k|n k∣n,且将 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+2…s11s12…s1i。
题解:
我们可以枚举所有 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;
}