zoukankan      html  css  js  c++  java
  • hdu3374最小表示法+KMP

    题意:
          给你一个最长100W的串,然后让你找到最小同构子串,还有最大同构子串的下标,最小同构子串就是把字符串连接成一个环,然后选择一个地方断开,得到的一个ASCII最小的子串(求最大同理),得到两个下标之后还要求两个数,就是最小子串出现的次数,还有最大子串出现的次数,就是所有循环移位后的到的len个子串中最小子串出现了多少次?


    思路:
          求最小和最大小标这个可以用最小表示法来求(求最大就是把最小表示法稍微改一下),而求出现次数可以用KMP来求,出现次数等于最大周期数,为什么等于这个可以这样想
    abcabcabc 循环移位 bcabcabca 继续 cabcabcab 继续 abcabcabc三次就得到一个一样的了,那么也就是说这个串中的任何循环移位串只要三次就会出现一样的,本来总个数是len,每三个一样是不是就是每个串出现len/3,这个三是不是就是我们KMP里面说的那个最小循环节。len/最小循环节 不就是最小循环节的周期数吗? so..直接KMP搞定次数,任何串出现的次数都是一样的,最后再返回来说下最小表示法。


    最小表示法可以直接求出来自小的(或者是最大的)循环移位串的首字母是的下标,
    比如dabc的小标就是2(从1开始)
    核心代码很短
    int GetMinId(char *str)
    {
        int i = 0 ,j = 1 ,k = 0;
        while(i < len && j < len && k < len)
        { 
            int t = str[(i+k)%len] - str[(j+k)%len];
            if(!t) ++k;
            else 
            {
                t > 0 ? i = i + k + 1 : j = j + k + 1;
                if(i == j) j ++;
                k = 0;
            } 
        }
        return i < j ? i : j;


    }


    可以这么理解,先定义两个变量i,j表示的都是前缀的下标,每次更新的时候我们把不满足的下标往后更新,就是往后+,最后得到前面的那个小的就是答案,关键是为什么?
    我是这样想的
    首先核心就是
    if(t > 0) i = i + k + 1;
    else j = j + k + 1;
    这个地方是什么情况,比如i代表的串是 abcd  j代表的串是abca此时的k肯定是3那么t>0这个时候i直接跳到d的后面,也就是i = i + k + 1,就是默认之间的都肯定不是答案,这个是关键,为什么之间的bcd都肯定不是答案的起点呢,原因是abcd 和abca比较的时候到k=3的时候发现不相等了,那么之前的一定是相等的,那么也就是说i的串的a和d之间的bcd当串首字母的时候肯定会被j的串abca中的bc当首字母比下去,因为bcd<bca cd< ca d < a就是没有必要再比较了,这个一开始可能很不容易理解,但是仔细想想会明白的,我说的是我自己的理解,也有可能有错误,还有就是提示一点,如果实在理解不了这个方法可以先写一个暴力的,然后在想,我就是这么干的,顺便给一个暴力的代码吧,暴力的很多时候也可以过题目,只不过要看数据。


    int GetMinId(char * str)
    {
       int len = stelen(str);
       int i = 0 ,j = 1 , k = 0;
       while(i < len && j < len && k < len)
       {
           int t = str[(i+k)%len] - str[(j+k)%len];
           if(!t) ++k;
           else 
           {
              if(t > 0) i = j;
              j ++;
              k = 0;
            }
       }
       return i;
    }




    下面是hdu3374代码


    #include<stdio.h>
    #include<string.h>


    #define N 1000000 + 10


    char str[N];
    int next[N];


    void GetNext(int m ,char *str)
    {
        int j = 0 ,k = -1;
        next[0] = -1;
        while(j < m)
        {
            if(k == -1 || str[j] == str[k])
            next[++j] = ++k;
            else k = next[k];
        }
    }


    int GetMinId(int len ,char *str)
    {
        int i = 0 ,j = 1 ,k = 0;
        while(i < len && j < len && k < len)
        {
            int t = str[(i+k)%len] - str[(j+k)%len];
            if(!t) ++k;
            else
            {
                t > 0 ? i = i + k + 1 : j = j + k + 1;
                if(i == j) j ++;
                k = 0;
            }
        }
        return i < j ? i : j;
    }


    int GetMaxId(int len ,char *str)
    {
        int i = 0 ,j = 1 ,k = 0;
        while(i < len && j < len && k < len)
        {
            int t = str[(i+k)%len] - str[(j+k)%len];
            if(!t) ++k;
            else
            {
                t < 0 ? i = i + k + 1 : j = j + k + 1;
                if(i == j) j ++;
                k = 0;
            }
        }
        return i < j ? i : j;
    }


    int main ()
    {
        int len;
        while(~scanf("%s" ,str))
        {
            len = strlen(str);
            GetNext(len ,str);
            int max = GetMaxId(len ,str) + 1;
            int min = GetMinId(len ,str) + 1;
            int c;
            if(next[len] && len % (len - next[len]) == 0)
            c = len / (len - next[len]);
            else c = 1;
            printf("%d %d %d %d " ,min ,c ,max ,c);
        }
        return 0;
    }
  • 相关阅读:
    未授权访问漏洞总结
    常见的端口速查
    Memcache未授权访问漏洞利用及修复
    Ubuntu 16.04 LTS安装Docker
    Ubuntu安装Python2+Python3
    不直接登录SharePoint服务器,通过远程直接部署WSP解决方案包
    SharePoint 2010 人员选择器搜索范围的限定
    SharePoint 2010 匿名访问开启后不能访问Allitems.aspx或DisplayForm.aspx
    博主跑路了
    【pwnable.tw】 alive_note
  • 原文地址:https://www.cnblogs.com/csnd/p/12062529.html
Copyright © 2011-2022 走看看