1. 程式人生 > >【擴充套件KMP】【HDU 4333】Revolving Digits

【擴充套件KMP】【HDU 4333】Revolving Digits

題意:

      給定一個字串,然後將字串最後的字母不斷移動到字串的首字母位置,需要求出在這個移動的過程中,生成的字串比原串字典序大、相等、小的所有字串個數。移動過程中,相同的字串不重複計算。

 

思路:

     由於是比較字典序,因此需要進行字首比較。自然地想到要使用擴充套件kmp知識,我們這裡先大概回憶一下擴充套件kmp知識。

          next [ i ] : 模板串從 i 開始擷取的子串與原串的最大字首匹配長度

          ex [ i ] : 匹配串從 i 開始擷取的子串與模板串的最大字首匹配長度

     可以用線性時間求出這兩個陣列。

 

     我們再來看這個題,就變成了一個水題。就是將這個串*2拼在一起,然後對於這個新串求一遍next陣列即可。現在問題在於如何處理 “相同的字串不重複計算” 這個問題,很自然地可以想到迴圈節。

     令 cnt = 串S迴圈節在串S中出現了幾次。

     則對於這個串可能出現的所有串中,每個串出現的重複次數為cnt。因此最後的答案需要除以cnt。

 

總結:

     擴充套件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開始擷取的子串與模板串的最大字首匹配長度
*/