zoukankan      html  css  js  c++  java
  • loj6158 A+B Problem (扩展KMP)

    题目:

    https://loj.ac/problem/6158

    分析:

    先把S串逆置,就是从低位向高位看

    我们再弄个T串,S串前面有x个连续的0,那么T串前面也有x个连续的0

    第x+1位,满足S[x+1]+T[x+1]=10

    后面的位置,均满足S[j]+T[j]=9

    然后我们发现S的每一个后缀S[i]与T串进行匹配,求个最长的前缀就是当前在这个位置劈开的结果

    这个是个典型的扩展KMP的应用,即对于S、T串,求S的每个后缀S[i]与T的最长公共前缀

    本题有几点细节

    1、因为最高位之前的那些位置都是0,所以在S串的后面,需要加上一些0

    2、要注意当某个S[i]和T的最长公共前缀超过了i的位置,那么超过i的那些位置并不是我们想要的,实际上,那些应该是99999999……或者000000........

    所以可以预处理出S的每一位后面有多少连续的0和多少个连续的9

     1 #include<cstring>
     2 #include<algorithm>
     3 #include<cstdio>
     4 using namespace std;
     5 const int maxn=1e6;
     6 char S[2*maxn+50],T[maxn+50];//S是母串,T是子串
     7 int len1,len2;
     8 int next[maxn+50],extend[2*maxn+50];//extend[i]表示S[i..len1-1]和T的最长公共前缀的长度,next[i]表示T[i..len2-1]和T的最长公共前缀的长度
     9 int num9[maxn+50];
    10 int num0[maxn+50];
    11 void getnext()
    12 {
    13     next[0]=len2;
    14     int j=0;
    15     while(j+1<len2&&T[j]==T[j+1]) ++j;
    16     next[1]=j;
    17     int k=1;
    18     for(int i=2;i<len2;++i)
    19     {
    20         int p=k+next[k]-1,l=next[i-k];
    21         if(i+l<p+1) next[i]=l;
    22         else
    23         {
    24             j=max(p-i+1,0);
    25             while(i+j<len2&&T[i+j]==T[j]) ++j;
    26             next[i]=j;
    27             k=i;
    28         }
    29     }
    30 }
    31 void ekmp()
    32 {
    33     int j=0;
    34     while(j<len1&&j<len2&&S[j]==T[j]) ++j;
    35     extend[0]=j;
    36     int k=0;
    37     for(int i=1;i<len1;++i)
    38     {
    39         int p=k+extend[k]-1,l=next[i-k];//p表示到达的最远位置,k是对应最远位置的i
    40         if(i+l<p+1) extend[i]=l;
    41         else
    42         {
    43             j=max(p-i+1,0);
    44             while(i+j<len1&&j<len2&&S[i+j]==T[j]) ++j;
    45             extend[i]=j;
    46             k=i;
    47         }
    48     }
    49 }
    50 int main()
    51 {
    52 
    53     while (scanf("%s",S)!=EOF)
    54     {
    55         len1=strlen(S);
    56         for(int i=0;i<len1/2;++i) swap(S[i],S[len1-i-1]);
    57         int i=0;
    58         for(i=0;S[i]=='0';++i) T[i]='0';
    59         T[i]='9'+1-S[i]+'0';
    60         ++i;
    61         for(i;i<len1;++i)
    62             T[i]='9'-S[i]+'0';
    63         T[len2=len1]='';
    64         for(int i=len1;i<len1+len2;++i) S[i]='0';
    65         S[len1+len2]='';
    66         len1=strlen(S);
    67         memset(num9,0,sizeof(num9));
    68         memset(num0,0,sizeof(num0));
    69         for(int i=len2-1;i>=0;--i)
    70         {
    71             if(S[i+1]!='9') num9[i]=0;else num9[i]=num9[i+1]+1;
    72             if(S[i+1]!='0'||i==len2-1) num0[i]=0;else num0[i]=num0[i+1]+1;
    73         }
    74         memset(next,0,sizeof(next));
    75         memset(extend,0,sizeof(extend));
    76         getnext();
    77         ekmp();
    78         int ans=0;
    79         for(int i=1;i<len2;++i)
    80         {
    81             if(extend[i]>=i)
    82             {
    83                 if(S[i]=='0'&&num0[i]>=i-1) ans=max(ans,num0[i+i-1]+i);
    84                 else ans=max(ans,num9[i+i-1]+i);
    85             }
    86             else
    87              ans=max(ans,extend[i]);
    88         }
    89         printf("%d
    ",ans);
    90     }
    91     return 0;
    92 }
    View Code
  • 相关阅读:
    HDU 3577Fast Arrangement(线段树模板之区间增减更新 区间求和查询)
    POJ 3468 A Simple Problem with Integers(线段树模板之区间增减更新 区间求和查询)
    HDU 1754 I Hate It(线段树之单点更新 区间最值查询)
    CSU 2056 a simple game (正反进行KMP)超级好题!!!
    HihoCoder
    HDU 6318 Swaps and Inversions 思路很巧妙!!!(转换为树状数组或者归并求解逆序数)
    51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)
    POJ 2299 Ultra-QuickSort 求逆序数 (归并或者数状数组)此题为树状数组入门题!!!
    C# DBHelper类 参考
    C#学习之接口
  • 原文地址:https://www.cnblogs.com/wmrv587/p/7103033.html
Copyright © 2011-2022 走看看