zoukankan      html  css  js  c++  java
  • 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] = '';
     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
    ", ans);
    142     }
    143     return 0;
    144 }
  • 相关阅读:
    04-老马jQuery教程-DOM节点操作及位置和大小
    03-老马jQuery教程-DOM操作
    02-老马jQuery教程-jQuery事件处理
    01-老马jQuery教程-jQuery入口函数及选择器
    08Vue.js快速入门-Vue综合实战项目
    09Vue.js快速入门-Vue入门之Vuex实战
    07Vue.js快速入门-Vue路由详解
    06Vue.js快速入门-Vue组件化开发
    整套高质量前端基础到高级视频教程免费发布
    05-Vue入门系列之Vue实例详解与生命周期
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9982651.html
Copyright © 2011-2022 走看看