2018ACM-ICPC南京区域赛M---Mediocre String Problem【exKMP】【Manacher】

杀马特。学长 韩版系。学妹 提交于 2020-11-03 05:39:33

这题就单独写个题解吧。想了两天了,刚刚问了一个大佬思路基本上有了。

题意:

一个串$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 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!