• hdu 4333"Revolving Digits"(KMP求字符串最小循环节+拓展KMP)


    传送门

    题意:

      此题意很好理解,便不在此赘述;

    题解:

      解题思路:KMP求字符串最小循环节+拓展KMP

      ①首先,根据KMP求字符串最小循环节的算法求出字符串s的最小循环节的长度,记为 k;

      ②根据拓展KMP求出字符串s的nex[]数组,那么对于由第 i 位打头构成的新数b,如何判断其与原数a的大小关系呢?

        1)如果 i%k == 0,那么b == a;

        2)如果 i%k ≠ 0 ,令L=nex[i],那么只需判断s[ i+L ]与s[ L ]的大小关系即可,需要注意的是,如果i+L = len呢?此时又该怎样处理呢?

        方法1:依次判断s[0,1,....] 与 s[ L,L+1,..... ]的关系,直到找出第一个不相等的位置判断其大小;

        方法2:判断 s[ nex[L] ]与s[ L+nex[L] ]的大小关系;

      如果采用方法1,很不幸,会超时,所以,方法2才是行之有效的方法;

      根据题意,此题让求得是不同的数,那么,如何去重呢?

      根据KMP已经求出了k,那么串s得循环周期为 len / k ,那么每种新数必然会重复 len / k次,只要在输出结果上将求出的答案除以 (len/k) 即可;

      还有一点需要注意的是,和原数相同的数,当且仅当只有一个,不论输入任何数,输出1即可;

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 const int maxn=1e6+50;
     6 
     7 char digit[maxn];
     8 int nex[maxn];
     9 
    10 int Period()
    11 {
    12     int len=strlen(digit);
    13     nex[0]=-1;
    14     nex[1]=0;
    15     int cnt=0;
    16     int index=2;
    17     while(index <= len)
    18     {
    19         if(digit[index-1] == digit[cnt])
    20             nex[index++]=++cnt;
    21         else if(cnt != 0)
    22             cnt=nex[cnt];
    23         else
    24             nex[index++]=0;
    25     }
    26     int k=len;
    27     if(len%(len-nex[len]) == 0 && nex[len] != 0)
    28         k=len-nex[len];
    29     return k;
    30 }
    31 void getNext()
    32 {
    33     int len=strlen(digit);
    34     nex[0]=len;
    35     int j=0;
    36     while(j+1 < len && digit[j+1] == digit[j])
    37         j++;
    38     nex[1]=j;
    39     int k=1;
    40     for(int i=2;i < len;++i)
    41     {
    42         int p=k+nex[k]-1;
    43         int l=nex[i-k];
    44         if(l < p-i+1)
    45             nex[i]=l;
    46         else
    47         {
    48             j=max(0,p-i+1);
    49             while(i+j < len && digit[i+j] == digit[j])
    50                 j++;
    51             k=i;
    52             nex[i]=j;
    53         }
    54     }
    55 }
    56 bool isLess(int i,int j,int len)
    57 {
    58     if(j == len)//如果j == len
    59     {
    60         j=nex[i];
    61         i=i+nex[i];
    62     }
    63     return (digit[j]-'0') < (digit[i]-'0');
    64 }
    65 void Solve()
    66 {
    67     int k=Period();//KMP求出最小循环节的长度
    68     getNext();//拓展KMP求解nex[]
    69 
    70     int ansL=0;
    71     int ansG=0;
    72     int len=strlen(digit);
    73     for(int i=1;i < len;++i)
    74     {
    75         int l=nex[i];
    76         if(i%k == 0)//与原数相等
    77             continue;
    78 
    79         if(isLess(l,i+l,len))//判断是否小于原数
    80             ansL++;
    81         else
    82             ansG++;
    83     }
    84     printf(" %d %d %d
    ",ansL/(len/k),1,ansG/(len/k));
    85 }
    86 int main()
    87 {
    88     int test;
    89     scanf("%d",&test);
    90     for(int kase=1;kase <= test;++kase)
    91     {
    92         scanf("%s",digit);
    93         printf("Case %d:",kase);
    94         Solve();
    95     }
    96     return 0;
    97 }
    View Code

      在网上看的其他人写的题解,有个很巧妙的方法:

      将字符串s拷贝一份加入到字符串s中,通过拓展KMP求出nex[]后,对于由第 i 打头构成的新数b:

      1)如果nex[i] > len/2,那么b == a;

      2)判断s[ i+nex[i] ]与s[ nex[i] ]的相对大小;


    分割线:2019.5.7

    省赛临近,重新温习了一下拓展KMP

    思路:

      定义串 s , t

      读入数据到 t 串,然后,复制两边 t 串到 s 串;

      以 s 为母串,t 为子串求解ext[]数组;

      遍历一遍串t,对于位置 i ,判断 t[ ext[i] ] 与 s[ i+ext[i] ] 的大小关系;

      前者大,L++,反之, G++;

      输出结果 L / k , 1 , G / k;

      k : 串 t 的循环节;

      之所以 / k 是因为可能有重复的;

    代码:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 using namespace std;
      5 const int maxn=2e5+50;
      6 
      7 char s[maxn];
      8 char t[maxn];
      9 
     10 struct KMP
     11 {
     12     int nex[maxn];///数组大小根据题意而定
     13     void getNex(const char *s)
     14     {
     15         int len=strlen(s);
     16         nex[0]=-1;
     17         nex[1]=0;
     18         int cnt=0;
     19         int index=2;
     20         while(index <= len)
     21         {
     22             if(s[index-1] == s[cnt])
     23                 nex[index++]=++cnt;
     24             else if(cnt != 0)
     25                 cnt=nex[cnt];
     26             else
     27                 nex[index++]=0;
     28         }
     29     }
     30     int F(const char *s)///返回串s的循环节
     31     {
     32         getNex(s);
     33         int len=strlen(s);
     34         int res=1;
     35         if(nex[len] != 0 && len%(len-nex[len]) == 0)
     36             res=len/(len-nex[len]);
     37         return res;///最小循环节长度 = len/res
     38     }
     39 }_kmp;
     40 /**
     41     拓展KMP
     42     nex[i]:t[0,...m-1]与t[i,...,m-1]的最长公共前缀
     43     ext[i]:s[i,...n-1]与t[0,...,m-1]的最长公共前缀
     44 */
     45 struct ExtendKMP
     46 {
     47     int nex[maxn];
     48     int ext[maxn];
     49     void getNex(const char *t)///预处理出t串的nex
     50     {
     51         int len=strlen(t);
     52         nex[0]=len;
     53         nex[1]=0;
     54         for(int i=1;i < len && t[i] == t[nex[1]];i++,nex[1]++);
     55         int K=1;
     56         for(int i=2;i < len;++i)
     57         {
     58             int L=nex[i-K];
     59             nex[i]=min(L,max(K+nex[K]-i,0));///K+nex[K]-i 可能小于0,所以两者取max,整体取min
     60             for(int j=i+nex[i];j < len && t[j] == t[nex[i]];j++,nex[i]++);
     61             if(K+nex[K] < i+nex[i])
     62                 K=i;
     63         }
     64     }
     65     void getExtend(const char *s,const char *t)
     66     {
     67         int n=strlen(s);
     68         int m=strlen(t);
     69         ext[0]=0;
     70         for(int i=0;i < n && i < m && s[i] == t[i];i++,ext[0]++);
     71         int K=0;
     72         for(int i=1;i < n;++i)
     73         {
     74             /**
     75                 P=K+ext[K]-1,最右边界
     76                 s[K,...,P] = t[0,.....,P-K]
     77                 s[i,...,P] = t[i-K,...,P-K]
     78                 t[i-K,....,i-K+L-1] = t[0,.......L-1]
     79             */
     80             int L=nex[i-K];
     81             ext[i]=min(L,max(K+ext[K]-i,0));
     82             for(int j=i+ext[i];j < n && ext[i] < m && s[j] == t[ext[i]];j++,ext[i]++);
     83             if(K+ext[K] < i+ext[i])
     84                 K=i;
     85         }
     86     }
     87 }_eKMP;
     88 
     89 void Solve()
     90 {
     91     _eKMP.getNex(t);
     92     _eKMP.getExtend(s,t);
     93 
     94     int L=0,G=0;
     95     int len=strlen(t);
     96     for(int i=1;i < len;++i)
     97     {
     98         int ext=_eKMP.ext[i];
     99         if(ext == len)
    100             continue;
    101         if(s[i+ext] > t[ext])
    102             G++;
    103         else
    104             L++;
    105     }
    106     int k=_kmp.F(t);///串t的循环节
    107     printf("%d 1 %d
    ",L/k,G/k);
    108 }
    109 int main()
    110 {
    111     int test;
    112     scanf("%d",&test);
    113     for(int kase=1;kase <= test;++kase)
    114     {
    115         scanf("%s",t);
    116         strcpy(s,t);
    117         strcat(s,t);///拷贝两边t串到s
    118         s[strlen(t)<<1]='';///可加可不加,最好加上
    119         printf("Case %d: ",kase);
    120         Solve();
    121     }
    122     return 0;
    123 }
    View Code
  • 相关阅读:
    置的测试文件的大小一定要大过你的内存(最佳为内存的两倍大小),不然linux会给你的读写的内容进行缓存,会使数值非常不准确。
    利用IOzone进行存储性能测试
    Linux下Firefox打开文件jnlp文件
    sed -i '14s/yes/no/' tftp
    创建用户(adduser和useradd)和删除用户(userdel)
    vs2015 调试 巨慢 卡顿的问题
    ajax.beginform控制器中实体为null的问题
    数据库日志文件——数据库“xxx”的事务日志已满,原因为“LOG_BACKUP”
    产品开发感悟
    System.Data.SqlClient.SqlException (0x80131904): EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 1,当前计数 = 0。 EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 1,当前计数 = 0。
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10539270.html
Copyright © 2020-2023  润新知