HDU 3746 Cyclic Nacklace(KMP最小循環子串)

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3746

題目大意:給定一個字符串s,求在左邊或右邊至少添加幾個字符,可以形成一個循環。

解:首先明確在左邊和右邊添加是一樣的,反過來就可以了。其次考慮幾種情況:

假定字符串長度都爲n,

1、s1…s2,對於任意相同長度的s1和s2兩個子串,s1均不等於s2。即Next[len] = 0,

需要添加n個,因爲前綴串和後綴串不匹配。再有規律也沒有用。


2、s1……s1,其中……部分大於0, 即Next[len] * 2 <= len,這個時候需要添加的長度爲……部分,即len-Next[len]*2.

顯然第一種情況也可符合上式,所以在程序設計時可以合併。


3、sksksk……ks,其中s爲任意非空串,k爲任意串且允許爲空。顯然,第二種是這一情況的特例。即sks。但是我們需要單獨討論。因爲前綴子串和後綴子串不發生重疊。當k爲空時,原串爲ssss……s.

(PS:“sksksksksk”實際上是sk作爲一個整體,屬於ssssssss的結構)

又如abcabca中,"s"爲a, “k”爲bc。 那麼最終有k串的長度爲需要添加的字符長度

如何求k串的長度呢。

例:對於sksksksks,

第一遍處理:前綴後綴爲:sksksksks;

取子串第二遍處理:前綴後綴爲:sksksks;

……

第三遍:前綴後綴爲:sksks;

發現sks就是第二種情況,直接計算出k的長度即可。

相當於一個遞歸(循環求k的過程)。


總結:

if    Next[len] * 2 <= len, ans = len-Next[len]*2;

else  dfs(len) ;

當然網上也有直接用了公式,畢竟這個自己想的,比較有印象。想必大家也是參考過網上的做法了,我就不羅嗦了。

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
char a[100005];
int nxt[100005];;
void getNext(int len)
{
    int i = 0, j = -1;
    nxt[0] = -1;
    while(i < len) {
        if(j == -1 || a[i] == a[j]) {
            nxt[++i] = ++j;
        }
        else {
            j = nxt[j];
        }
    }
}
int solve(int len) {
    int tmp = nxt[len] * 2;
    if(tmp <= len) {
        return len - tmp;
    }
    else {
        return solve(nxt[len]);
    }
}
int main ()
{
    int t;
    scanf("%d", &t);
    while (t --) {
        scanf("%s", a);
        int len = strlen(a);
        getNext(len);
        printf("%d\n", solve(len));
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章