安吉D3-T1
949 字
5 分钟
安吉D3-T1
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1本文
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
题目描述
书上记载,这两串密语可能拥有共同的“基础咒语”.如果一个短咒语 可以通过将自身连续拼接多次来完全构成字符串 ,同时也可以通过连续拼接多次完全构成字符串 (即 同时是 和 的循环节,或称为公共除数),那么 就是一个合法的“基础咒语”.
小信想要彻底解开魔法书的秘密,他需要计算出,到底有多少个不同的字符串 能够同时满足上述条件?
输入格式
第一行包含一个字符串 .
第二行包含一个字符串 .
保证两个字符串均只包含小写英文字母,且中间没有空格.
输出格式
输出一行一个整数,表示满足条件的字符串 的数量.如果不存在这样的字符串 ,则输出 0.
样例
输入 #1
abcdabcdabcdabcdabcdabcd输出 #1
2样例解释
对于样例 # 1,满足条件的“基础咒语” 有两个:
abcd:拼接 2 次可得 ,拼接 4 次可得 .abcdabcd:拼接 1 次可得 ,拼接 2 次可得 .
数据范围
| Subtask | 分值 | 数据范围 | 特殊性质 |
|---|---|---|---|
| 1 | 30 | 无 | |
| 2 | 30 | 保证两个字符串都由同一个较短字符串重复得到 | |
| 3 | 40 | 无 |
由题意可得,这两个字符串 、 的循环节的长度,一定是原来两个字符串长度最大公因数的因数.
因此,尝试枚举循环节的长度:
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); } }}对于每一个长度 ,只需要验证:
- 是 (设长度为 ) 的循环节,即 去掉前面一节循环节和去掉后面一节循环节是相同的:
- 是 的循环节,同上.
所有的字符串判等可以使用哈希优化。
标程
以下标程使用双哈希来快速进行字符串判等。
#include <bits/stdc++.h>using namespace std;#define int long longconst 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;}一般的,对循环节的判断可以使用哈希轻松解决。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


