題目鏈接: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;
}