【扩展KMP】【HDU 4333】Revolving Digits

题意:

      给定一个字符串,然后将字符串最后的字母不断移动到字符串的首字母位置,需要求出在这个移动的过程中,生成的字符串比原串字典序大、相等、小的所有字符串个数。移动过程中,相同的字符串不重复计算。

思路:

     由于是比较字典序,因此需要进行前缀比较。自然地想到要使用扩展kmp知识,我们这里先大概回忆一下扩展kmp知识。

          next [ i ] : 模板串从 i 开始截取的子串与原串的最大前缀匹配长度

          ex [ i ] : 匹配串从 i 开始截取的子串与模板串的最大前缀匹配长度

     可以用线性时间求出这两个数组。

     我们再来看这个题,就变成了一个水题。就是将这个串*2拼在一起,然后对于这个新串求一遍next数组即可。现在问题在于如何处理 “相同的字符串不重复计算” 这个问题,很自然地可以想到循环节。

     令 cnt = 串S循环节在串S中出现了几次。

     则对于这个串可能出现的所有串中,每个串出现的重复次数为cnt。因此最后的答案需要除以cnt。

扫描二维码关注公众号,回复: 4129447 查看本文章

总结:

     扩展KMP就是用来计算前缀匹配长度的,应用于各类前缀匹配问题。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
typedef long long ll;
const int INF = 2147483640;
const double eps = 1e-8;
const int N = 3*100000+100;   //字符串长度最大值

char a[N];
int nxt[N]; //ex数组即为extend数组
//预处理计算next数组

int get(char* tp)
{
    int nxtt[N];
    char s[N];
    memset(nxtt,0,sizeof nxtt);
    int l = strlen(tp);
    for(int i = l+1; i >= 1; i--) s[i] = tp[i-1];
    int j = 0;
    for(int i = 2;i <= l;i ++)
    {
        while(j && s[j+1] != s[i])  j = nxtt[j];
        if(s[j+1] == s[i])  j ++;
        nxtt[i] = j;
    }
    return l-nxtt[l];
}

void GETNEXT(char *str)
{
    int i=0,j,po,len=strlen(str);
    nxt[0]=len;//初始化next[0]
    while(str[i]==str[i+1]&&i+1<len)//计算next[1]
    i++;
    nxt[1]=i;
    po=1;//初始化po的位置
    for(i=2;i<len;i++)
    {
        if(nxt[i-po]+i<nxt[po]+po)//第一种情况,可以直接得到next[i]的值
        nxt[i]=nxt[i-po];
        else//第二种情况,要继续匹配才能得到next[i]的值
        {
            j=nxt[po]+po-i;
            if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
            while(i+j<len&&str[j]==str[j+i])//计算next[i]
            j++;
            nxt[i]=j;
            po=i;//更新po的位置
        }
    }
}

int main(int argc, char const *argv[])
{
    int T;
    scanf("%d",&T);
    rep(kk,1,T)
    {
        scanf("%s",a);
        int len = strlen(a);
        int tp = get(a);
        int ans = len/tp;
    //    if(len % tp == 0) ans = len/tp;
        rep(i,len,2*len-1)
            a[i] = a[i-len];
        a[2*len] = '\0';
        GETNEXT(a);
        int c1 = 0,c2 = 0,c3 = 0;
        rep(i,0,len-1)
        {
            if(a[i] == '0'){
                c1++;
                continue;
            }
            int tp = nxt[i];
            if(tp >= len) c2++;
            else{
                if(a[tp]-'0' < a[i+tp]-'0') c3++;
                else c1++;
            }
        }
        printf("Case %d: ",kk);
        printf("%d %d %d\n",c1/ans,c2/ans,c3/ans);
    }
    return 0;
}

/*
    字符串从0开始编号
    next[i]: 模板串从i开始截取的子串与原串的最大前缀匹配长度
    ex[i]: 匹配串从i开始截取的子串与模板串的最大前缀匹配长度
*/

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/83791986
今日推荐