0%

2026 杭电春季联赛-1003-回文串

题目大意

题目描述
给定两个长度均为 nn 的小写字母字符串 SSTT
你需要在 SS 中选取一个子串 SS',在 TT 中选取一个子串 TT'(空串不算作子串),并将 TT' 拼接在 SS' 的后面组成一个新的字符串。
求共有多少种选取方式,使得拼接后的新字符串是一个回文串。
注:选取方式由子串在原串中的起始和终止位置决定;回文串指正读和反读都相同的字符串。

输入格式
第一行输入一个整数表示测试组数 TTT10T \le 10)。
接下来每组数据输入两行,分别表示字符串 SSTT
数据保证两个字符串长度相等且 n2000n \le 2000

输出格式
对于每组数据,输出一行整数,表示能组成回文串的子串选取方案数。

样例

样例输入

1
2
3
4
5
2
ab
ac
aba
bba

样例输出

1
2
2
15

样例解释
对于第一组样例,S="ab"S = \text{"ab"}T="ac"T = \text{"ac"}。合法的选取方式共 22 种:

  1. 选取 S[11]="a"S[1 \dots 1] = \text{"a"}T[11]="a"T[1 \dots 1] = \text{"a"},拼接得到 "aa"\text{"aa"},是回文串。

  2. 选取 S[12]="ab"S[1 \dots 2] = \text{"ab"}T[11]="a"T[1 \dots 1] = \text{"a"},拼接得到 "aba"\text{"aba"},是回文串。

对于第二组样例,S="aba"S = \text{"aba"}T="bba"T = \text{"bba"}。合法的选取方式共 1515 种,列举其中几种情况:

  • 选取 S[11]="a"S[1 \dots 1] = \text{"a"}T[33]="a"T[3 \dots 3] = \text{"a"},拼接得到 "aa"\text{"aa"}

  • 选取 S[22]="b"S[2 \dots 2] = \text{"b"}T[11]="b"T[1 \dots 1] = \text{"b"},拼接得到 "bb"\text{"bb"}

  • 选取 S[22]="b"S[2 \dots 2] = \text{"b"}T[22]="b"T[2 \dots 2] = \text{"b"},拼接得到 "bb"\text{"bb"}

  • 选取 S[12]="ab"S[1 \dots 2] = \text{"ab"}T[23]="ba"T[2 \dots 3] = \text{"ba"},拼接得到 "abba"\text{"abba"}

  • 选取 S[13]="aba"S[1 \dots 3] = \text{"aba"}T[23]="ba"T[2 \dots 3] = \text{"ba"},拼接得到 "ababa"\text{"ababa"}
    以此类推,满足条件的下标区间组合总共有 1515 种。

思路讲解

完全理解 manacher

image

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
void Solve() {
string s;
string t;
cin >> s >> t;
string rev_t = t;
reverse(all(rev_t));
ll szs = SZ(s), szt = SZ(t);
vector<ll> ps = manacher(s);
vector<ll> pt = manacher(rev_t);
// 以 i 为起点的这个回文串数量
vector<ll> cntS(szs + 3), cntT(szt + 3);
for (int i = 0; i < SZ(ps); ++i) {
ll r = ps[i] / 2;
if (r == 0) {
continue;
}
ll pos = i / 2;
cntS[pos - r + 1]++;
cntS[pos + 1]--;
}
for (int i = 0; i < SZ(pt); ++i) {
ll r = pt[i] / 2;
if (r == 0) {
continue;
}
ll pos = i / 2;
cntT[pos - r + 1]++;
cntT[pos + 1]--;
}
// 上面是在
partial_sum(all(cntS), cntS.begin());
partial_sum(all(cntT), cntT.begin());
#ifdef LOCAL
debug1d(cntS);
debug1d(cntT);
#endif
// 最长公共子后缀
vector<vector<ll> > lcsuf(szs, vector<ll>(szt));
ll ans = 0;
for (int i = 0; i < szs; ++i) {
for (int j = 0; j < szt; ++j) {
ll lans = 0;
if (i - 1 >= 0 && j - 1 >= 0) {
lans = lcsuf[i - 1][j - 1];
}
lcsuf[i][j] = lans;
if (s[i] == rev_t[j]) {
lcsuf[i][j]++;
} else {
lcsuf[i][j] = 0;
}
// 加一是因为还有空的情况,即 s 和 rev_t 都不提供 B
ll add = lcsuf[i][j] * (cntS[i + 1] + cntT[j + 1] + 1);
ans += add;
}
}
cout << ans << "\n";
}

AC代码

TLE,这个代码是这个 O(n2)O(n^2) 的,但是还是这个哈希表还是有点慢,被卡了。

AC
https://acm.hdu.edu.cn/contest/view-code?cid=1197&rid=19239

心路历程(WA,TLE,MLE……)