传送门: https://ac.nowcoder.com/acm/contest/9984/B
题意
给 字 符 串 s 和 t , 问 有 多 少 种 s 的 两 个 前 缀 相 连 是 t 的 前 缀 的 个 数 。 给字符串s和t,问有多少种s的两个前缀相连是t的前缀的个数。 给字符串s和t,问有多少种s的两个前缀相连是t的前缀的个数。
思路
首
先
这
题
暴
力
n
2
肯
定
会
T
的
。
首先这题暴力n^2肯定会T的。
首先这题暴力n2肯定会T的。
正
确
解
法
是
先
找
s
的
前
缀
s
1
和
t
的
前
半
部
分
配
对
,
再
找
s
的
最
长
前
缀
s
2
和
t
的
后
半
部
分
配
对
,
贡
献
为
s
2
的
长
度
。
正确解法是先找s的前缀s1和t的前半部分配对,再找s的最长前缀s2和t的后半部分配对,贡献为s2的长度。
正确解法是先找s的前缀s1和t的前半部分配对,再找s的最长前缀s2和t的后半部分配对,贡献为s2的长度。
如
何
配
对
?
如何配对?
如何配对?
区
间
字
符
串
哈
希
!
区间字符串哈希!
区间字符串哈希!
关于字符串哈希,就是将一个区间的字符段用一个整数表示,在没有哈希冲突的情况下,这个整数就代表独一无二的字符段。
如
何
找
最
长
前
缀
?
如何找最长前缀?
如何找最长前缀?
二
分
!
二分!
二分!
Code
#include "bits/stdc++.h"
using namespace std;
typedef unsigned long long ull;
const ull mod = 131;
const int N = 1e5 + 10;
ull p[N];
ull sums[N], sumt[N];
char s[N], t[N];
int lens, lent;
ull hashs(int l, int r) {
return sums[r] - sums[l - 1] * p[r - l + 1];
}
ull hasht(int l, int r) {
if(r > lent) return 0;
return sumt[r] - sumt[l - 1] * p[r - l + 1];
}
void solve() {
cin >> (s + 1) >> (t + 1);
lens = strlen(s + 1), lent = strlen(t + 1);
p[0] = 1;
for(int i = 1;i <= max(lens, lent); i++) {
if(i <= lens) sums[i] = sums[i - 1] * mod + s[i];
if(i <= lent) sumt[i] = sumt[i - 1] * mod + t[i];
p[i] = p[i - 1] * mod;
}
ull ans = 0;
for(int i = 1;i <= lent - 1; i++) {
if(s[i] != t[i]) break; // 先匹配第一个s1
if(t[i + 1] != s[1]) continue;
int l = 1, r = lens, res = 0;
while(l <= r) { // 二分找出最长前缀
int mid = (l + r) / 2;
if(hashs(1, mid) == hasht(i + 1, i + mid)) {
res = mid; l = mid + 1;
}
else r = mid - 1;
}
ans += res;
}
cout << ans << endl;
}
signed main() {
solve();
}