2018ACM-ICPC南京区域赛M---Mediocre String Problem【exKMP】【Manacher】
这题就单独写个题解吧。想了两天了,刚刚问了一个大佬思路基本上有了。
题意:
一个串$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 }