视频加载失败

安吉D3-T1

949 字
5 分钟
安吉D3-T1
原题呈现

题目描述#

书上记载,这两串密语可能拥有共同的“基础咒语”.如果一个短咒语 tt 可以通过将自身连续拼接多次来完全构成字符串 s1s_1,同时也可以通过连续拼接多次完全构成字符串 s2s_2(即 tt 同时是 s1s_1s2s_2 的循环节,或称为公共除数),那么 tt 就是一个合法的“基础咒语”.

小信想要彻底解开魔法书的秘密,他需要计算出,到底有多少个不同的字符串 tt 能够同时满足上述条件?

输入格式#

第一行包含一个字符串 s1s_1

第二行包含一个字符串 s2s_2

保证两个字符串均只包含小写英文字母,且中间没有空格.

输出格式#

输出一行一个整数,表示满足条件的字符串 tt 的数量.如果不存在这样的字符串 tt,则输出 0

样例#

输入 #1#

abcdabcd
abcdabcdabcdabcd

输出 #1#

2

样例解释#

对于样例 # 1,满足条件的“基础咒语” tt 有两个:

  • abcd:拼接 2 次可得 s1s_1,拼接 4 次可得 s2s_2
  • abcdabcd:拼接 1 次可得 s1s_1,拼接 2 次可得 s2s_2

数据范围#

Subtask分值数据范围特殊性质
1301s1,s21031\le s_1, s_2 \le 10^3
2301s1,s21051\le s_1, s_2 \le 10^5保证两个字符串都由同一个较短字符串重复得到
3401s1,s21071\le s_1, s_2 \le 10^7

由题意可得,这两个字符串 s1s_1s2s_2 的循环节的长度,一定是原来两个字符串长度最大公因数的因数.

因此,尝试枚举循环节的长度:

int maxLen = gcd(sx.size(), sy.size());
vector<int> fac;
for (int i = 1; i <= sqrt(maxLen); i++) {
if (maxLen % i == 0) {
fac.push_back(i);
if (i * i != maxLen) {
fac.push_back(maxLen / i);
}
}
}

对于每一个长度 dd,只需要验证:

  • s1[0..d1]s_1[0..d-1]s1s_1(设长度为 nn) 的循环节,即 s1s_1 去掉前面一节循环节和去掉后面一节循环节是相同的:s1[0,nd1]=s1[d,n1]s_1[0, n - d - 1] = s_1[d, n - 1]
  • s2[0..d1]s_2[0..d-1]s2s_2 的循环节,同上.
  • s1[0..d1]=s2[0..d1]s_1[0..d-1]=s_2[0..d-1]

所有的字符串判等可以使用哈希优化。

标程

以下标程使用双哈希来快速进行字符串判等。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int BASE = 128;
const int MOD1 = 1e9 + 7;
const int MOD2 = 1e9 + 9;
const int N = 1e7 + 10;
int la1[N], la2[N], lb1[N], lb2[N], p1[N], p2[N];
string sx, sy;
struct Hash {
int *h;
int *p;
int MOD;
Hash(int *h, int *p, int MOD) {
this->h = h;
this->p = p;
this->MOD = MOD;
}
void init(string &str) {
p[0] = 1;
for (int i = 1; i <= str.size(); i++) {
(h[i] = 1ll * h[i - 1] * BASE % MOD + str[i - 1]) %= MOD;
p[i] = p[i - 1] * BASE % MOD;
}
}
/**
* 获取子串 hash,0-based
*/
int get(int l, int r) {
r++, l++; // 转成 1-based
return (h[r] - (1ll * h[l - 1] * p[r - l + 1] % MOD) + MOD) % MOD;
}
};
Hash hx1(la1, p1, MOD1), hx2(la2, p2, MOD2), hy1(lb1, p1, MOD1),
hy2(lb2, p2, MOD2);
int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
signed main() {
freopen("magic.in", "r", stdin);
freopen("magic.out", "w", stdout);
cin >> sx >> sy;
hx1.init(sx), hx2.init(sx), hy1.init(sy), hy2.init(sy);
int maxLen = gcd(sx.size(), sy.size());
vector<int> fac;
for (int i = 1; i <= sqrt(maxLen); i++) {
if (maxLen % i == 0) {
fac.push_back(i);
if (i * i != maxLen) {
fac.push_back(maxLen / i);
}
}
}
int ans = 0;
for (int i = 0; i < fac.size(); i++) {
int len = fac[i];
// 判断是否是循环节
if (!(hx1.get(0, sx.size() - len - 1) == hx1.get(len, sx.size() - 1) &&
hx2.get(0, sx.size() - len - 1) == hx2.get(len, sx.size() - 1)))
continue;
if (!(hy1.get(0, sy.size() - len - 1) == hy1.get(len, sy.size() - 1) &&
hy2.get(0, sy.size() - len - 1) == hy2.get(len, sy.size() - 1)))
continue;
// 判断循环节是否在两个子串中都相等
if (!(hx1.get(0, len - 1) == hy1.get(0, len - 1) &&
hx2.get(0, len - 1) == hy2.get(0, len - 1)))
continue;
ans++;
}
cout << ans;
}

一般的,对循环节的判断可以使用哈希轻松解决。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D3-T1
https://blog.jerrylab.top/posts/problem/anji2026/D3/T1/
作者
Jerry
发布于
2026-08-03
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0