这题就单独写个题解吧。想了两天了,刚刚问了一个大佬思路基本上有了。
题意:
一个串$S$,一个串$T$,在$S$中选一段子串$S[i,j]$,在$T$中选一段前缀$T[1,k]$使得$S[i,j]T[1,k]$拼起来得到的字符串是回文,并且$S$的这个串长度大于$T$的这个。问有多少这样的三元组$(i,j,k)$
思路:
首先我们可以知道我们要找的其实就是这样三个串,$a,b,c$。其中$a$和$c$合起来是$S$中连续的一段子串,$b$在$T$中且$a$和$b$是对称的,$c$一定要是一个回文,且长度至少是$1$。
第一步比较简单我们可以用manacher求出$S$中的每一个回文。
比如上面图中的下面话的是一个以$i$为中心的回文,假设他的半径是$p$。
那么$i-p$到$i-1$都是满足条件的$a$串的起始点,因为他们后面都接着一段回文。
那么我们把$S$倒过来得到$S'$,拿$S'$和$T$跑exkmp,就可以得到$S'$的每一个后缀和$T$最长公共前缀。
这表示有$ex[i]$个串可以作为$a$串的选择。
答案应该是$a$串的选择个数$*c$串的选择个数
$c$串的选择个数怎么找呢,其实他就是以$i$为开头的回文串的个数。
用manacher加差分可以处理。具体的可以看hdu5157 https://www.cnblogs.com/wyboooo/p/9988397.html这道题。
对于$S$串的每一个下标$i$,$ex[lens - i - 1 + 1] = k$表示$S[i-1-k+1,i-1]$和$T[1,k]$对称。
由于我算$pre$数组的时候把下标往后挪了一个 所以每一个下标$i$的贡献是$ex[lens - i + 1] * pre[i]$
这个下标对应的算清楚,记得用上long long 就可以过啦!耶!
搞了两天我终于写出来了!
妈呀真的好激动啊!!!!!
1 #include<iostream>
2 //#include<bits/stdc++.h>
3 #include<cstdio>
4 #include<cmath>
5 //#include<cstdlib>
6 #include<cstring>
7 #include<algorithm>
8 //#include<queue>
9 #include<vector>
10 //#include<set>
11 //#include<climits>
12 //#include<map>
13 using namespace std;
14 typedef long long LL;
15 #define N 100010
16 #define pi 3.1415926535
17 #define inf 0x3f3f3f3f
18
19 const int maxn = 1e6 + 5;
20 char s[maxn], ss[maxn * 2], t[maxn], s_rev[maxn];
21 LL pre[maxn * 2];
22 int lens, lent, p[maxn * 2];
23
24 int init()
25 {
26 ss[0] = '$';
27 ss[1] = '#';
28 int lenss = 2;
29 for(int i = 0; i < lens; i++){
30 ss[lenss++] = s[i];
31 ss[lenss++] = '#';
32 }
33 ss[lenss] = '\0';
34 return lenss;
35 }
36
37 void manacher()
38 {
39 int lenss = init();
40 int id, mx = 0;
41 for(int i = 1; i < lenss; i++){
42 if(i < mx){
43 p[i] = min(p[2 * id - i], mx - i);
44 }
45 else{
46 p[i] = 1;
47 }
48 while(ss[i - p[i]] == ss[i + p[i]])p[i]++;
49 if(mx < i + p[i]){
50 id = i;
51 mx = i + p[i];
52 }
53 }
54 }
55
56 int nxt[maxn],ex[maxn]; //ex数组即为extend数组
57 //预处理计算next数组
58 void GETNEXT(char *str)
59 {
60 int i=0,j,po,len=strlen(str);
61 nxt[0]=len;//初始化next[0]
62 while(str[i]==str[i+1]&&i+1<len)//计算next[1]
63 i++;
64 nxt[1]=i;
65 po=1;//初始化po的位置
66 for(i=2;i<len;i++)
67 {
68 if(nxt[i-po]+i<nxt[po]+po)//第一种情况,可以直接得到next[i]的值
69 nxt[i]=nxt[i-po];
70 else//第二种情况,要继续匹配才能得到next[i]的值
71 {
72 j=nxt[po]+po-i;
73 if(j<0)j=0;//如果i>po+nxt[po],则要从头开始匹配
74 while(i+j<len&&str[j]==str[j+i])//计算next[i]
75 j++;
76 nxt[i]=j;
77 po=i;//更新po的位置
78 }
79 }
80 }
81 //计算extend数组
82 void EXKMP(char *s1,char *s2)
83 {
84 int i=0,j,po,len=strlen(s1),l2=strlen(s2);
85 GETNEXT(s2);//计算子串的next数组
86 while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]
87 i++;
88 ex[0]=i;
89 po=0;//初始化po的位置
90 for(i=1;i<len;i++)
91 {
92 if(nxt[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
93 ex[i]=nxt[i-po];
94 else//第二种情况,要继续匹配才能得到ex[i]的值
95 {
96 j=ex[po]+po-i;
97 if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
98 while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
99 j++;
100 ex[i]=j;
101 po=i;//更新po的位置
102 }
103 }
104 }
105
106
107 int main()
108 {
109
110 while(scanf("%s", s) != EOF){
111 scanf("%s", t);
112 lens = strlen(s);
113 lent = strlen(t);
114 for(int i = 0; i <= lens * 2 + 2; i++){
115 pre[i] = 0;
116 p[i] = 0;
117 ex[i] = 0;
118 }
119 manacher();
120 for(int i = lens * 2; i >= 2; i--){
121 int x = i / 2;
122 pre[x]++;
123 pre[x - (p[i] / 2)]--;
124 }
125 for(int i = lens; i >= 1; i--){
126 pre[i] += pre[i + 1];
127 }
128
129 for(int i = 0; i <= lens; i++){
130 s_rev[i] = s[lens - 1 - i];
131 }
132 EXKMP(s_rev, t);
133 LL ans = 0;
134 /*for(int i = 1; i <= lens; i++){
135 cout<<pre[i]<<" "<<ex[i]<<endl;
136 }*/
137 for(int i = 1; i <= lens; i++){
138 //if(ex[lens - i + 1])
139 ans += 1LL * ex[lens - i + 1] * pre[i];
140 }
141 printf("%I64d\n", ans);
142 }
143 return 0;
144 }
来源:oschina
链接:https://my.oschina.net/u/4288933/blog/3746520