NOI2016 優秀的拆分 字尾陣列

2022-04-29 23:24:08 字數 3130 閱讀 7678

題目鏈結:洛谷點我:-)

uoj點我:-)

題目描述

如果乙個字串可以被拆分為 aabb 的形式,其中 a和 b是任意非空字串,則我們稱該字串的這種拆分是優秀的。

例如,對於字串 aabaabaa,如果令 a=aab,b=a,我們就找到了這個字串拆分成 aabb的一種方式。

乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 a=a,b=baa,也可以用 aabb表示出上述字串;但是,字串 abaabaa 就沒有優秀的拆分。

現在給出乙個長度為 n(n <= 30000)的字串 s,我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這裡的子串是指字串中連續的一段。

以下事項需要注意:

1.出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。

2.在乙個拆分中,允許出現 a=b。例如 cccc 存在拆分 a=b=c。

3.字串本身也是它的乙個子串。

輸入格式

每個輸入檔案包含多組資料。輸入檔案的第一行只有乙個整數 t,表示資料的組數。保證 1≤t≤10。

接下來 t行,每行包含乙個僅由英文小寫字母構成的字串 s,意義如題所述。

輸出格式

輸出 t行,每行包含乙個整數,表示字串 s 所有子串的所有拆分中,總共有多少個是優秀的拆分。

思路

求兩個陣列:st[i]與en[i],分別表示以i這個字元開頭與以i這個字元結尾的『aa』形式的串有多少個,那麼答案就是∑n

−1i=

1st[

i+1]

∗en[

i],難點在於如何求出這兩個陣列:

我們列舉乙個長度

l表示當前找的『aa』型串的長度的一半,列舉i=

k∗l,

j=i+

l, 記x

=lcp

(suf

(i),

suf(

j)), 記y=

lcs(

pre(

i−1)

,pre

(j−1

)), 如果x+

y>=l,記

t=x+

y−l+

1,表示我們找到了t個長度為2l

的』aa『串

(自己可以舉個例子看看,如』abcabcab『)

為了方便理解,假設x+

y=l,那麼我們恰好找到乙個(i

−y,j

+x−1

)的』aa『串。

但是每次有乙個連續區間,我們不能乙個乙個加上,因為時效會出問題。所以用到差分的思想(當然有閒心寫線段樹也是可以的),在區間開始的地方加一,在區間結束的後乙個位置減一,那麼最後做一遍字首和即可。

注意:每次要清c1, c2(即x, y)陣列,因為後面的「y[sa[i]+k]==y[sa[i-1]+k]」可能會越限

感想

這題最開始用kmp寫了個95分暴力,覺得可以了,就沒做了。然後某一天發現小夥伴都寫了滿分啊,嚇得我趕緊寫

真心想不出正解,然而網上的題解又很敷衍,幸虧有dy大神的幫助,不然我就做不出來了qwq。有點難理解,其實也還好啦。但是。。。正解是怎麼想到的qwq

畢竟**不難寫,所以妙妙以後要多練思維(-_-|||)

想想,覺得本題也可以用hash解決,找個時間寫寫把**再貼上來吧。。。

**

95分大暴力

//miaomiao 2016.8.2

#include

#include

#include

#include

#include

#include

#include

using

namespace

std;

#define maxs (2000+5)

int f[maxs][maxs];

vector

xh[maxs];

void init()

void get_fail(int now, char *s)

}char s[maxs];

int main()

}int ans = 0;

for(int i = 0; i < slen; i++)

}printf("%d\n", ans);

}return

0;}

看了題解的正解

//miaomiao 2017.1.28

#include

#include

#include

#include

#include

using

namespace

std;

#define ll long long

#define set(a, v) memset(a, v, sizeof(a))

#define for(i, a, b) for(int i = (a); i <= (int)(b); i++)

#define forr(i, a, b) for(int i = (a); i >= (int)(b); i--)

#define log (15+5)

#define maxn (30000+5)

int n, c[maxn], c1[maxn], c2[maxn], log[maxn];

struct suffixarray

void buildsa(int m='z')

}void getheight()

for(j, 1, 15) for(i, 1, n)

}int lcp(int x, int y)}}

for(i, 1, n) st[i]+=st[i-1], en[i]+=en[i-1];

ll ans = 0;

for(i, 1, n) ans += 1ll*en[i]*st[i+1];

printf("%lld\n", ans);

}return

0;}

NOI2016優秀的拆分 字尾陣列

題目描述 如果乙個字串可以被拆分為 aabb 的形式,其中 a和 b是任意非空字串,則我們稱該字串的這種拆分是優秀的。例如,對於字串 aabaabaa,如果令 a aab,b a,我們就找到了這個字串拆分成 aabb的一種方式。乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 a...

字尾陣列 NOI2016 優秀的拆分

如果乙個字串可以被拆分為 aa bb的形式,其中 a 和 b 是任意非空字串,則我們稱該字串的這種拆分是優秀的。例如,對於字串 aabaabaa,如果令 a aab,b a,我們就找到了這個字串拆分成 aa bb的一種方式。乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 a a...

NOI2016 優秀的拆分(字尾陣列)

如果乙個字串可以被拆分為aabbaabb的形式,其中 a和 b是任意非空字串,則我們稱該字串的這種拆分是優秀的。例如,對於字串aabaabaa,如果令 a aab,b a,我們就找到了這個字串拆分成 aabb的一種方式。乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 a a,b...