如果乙個字串可以被拆分為 $aabb$ 的形式,其中 $a$ 和 $b$ 是任意非空字串,則我們稱該字串的這種拆分是優秀的。
例如,對於字串 aabaabaa,如果令 $a = \mathrm$,$b = \mathrm$,我們就找到了這個字串拆分成 $aabb$ 的一種方式。
乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令 $a=\mathrm$,$b=\mathrm$,也可以用 $aabb$ 表示出上述字串;但是,字串 abaabaa 就沒有優秀的拆分。
現在給出乙個長度為 $n$ 的字串 $s$,我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這裡的子串是指字串中連續的一段。
以下事項需要注意:
出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。
在乙個拆分中,允許出現 $a=b$。例如 cccc 存在拆分 $a=b=\mathtt$。
字串本身也是它的乙個子串。
每個輸入檔案包含多組資料。輸入檔案的第一行只有乙個整數 $t$,表示資料的組數。保證 $1 \le t \le 10$。
接下來 $t$ 行,每行包含乙個僅由英文小寫字母構成的字串 $s$,意義如題所述。
輸出 $t$ 行,每行包含乙個整數,表示字串 $s$ 所有子串的所有拆分中,總共有多少個是優秀的拆分。
對於全部的測試點,保證 $1 \le t \le 10$。以下對資料的限制均是對於單組輸入資料而言的,也就是說同乙個測試點下的 $t$ 組資料均滿足限制條件。
我們假定 $n$ 為字串 $s$ 的長度,每個測試點的詳細資料範圍見下表:
測試點編號
$n$其他約束
1、2$\leq 300$
$s$中所有字元全部相同
3、4$\leq 2000$
5、6$\leq 10$
無7、8
$\leq 20$
9、10
$\leq 30$
11、12
$\leq 50$
13、14
$\leq 100$
15$\leq 200$
16$\leq 300$
17$\leq 500$
18$\leq 1000$
19$\leq 2000$
20$\leq 30000$
似乎暴力就有95分啊?
先\(o(n^2)\)預處理雙hash,用來判斷子串是否相同
然後\(o(n^2)\)處理\(f[i]\),\(f[i]\)表示結尾位置為\(i\),滿足\(aa\)的子串數量
然後可以直接根據\(f[i]\)在\(o(n^2)\)的的時間內得到\(g[i]\),\(g[i]\)表示,結尾為i滿足\(aabb\)的子串數量
很基礎啊?
#include#include#include#include#include#include#include#include#include#include#define ll long long
using namespace std;
inline char nc()
return *p1++;}
inline void read(int &x)
inline void read(ll &x)
inline int read(char *s)
inline void read(char &x)
int wt,ss[19];
inline void print(int x)
}inline void print(ll x)
}int t,n,f1[2010][2010],f2[2010][2010],f[2010],g[2010];
char s[2010];
const int mo1=100271,mo2=500179;
void init()
}int main()
} for (int i=3;i<=n;i++)
}int ans=0;
for (int i=4;i<=n;i++)
ans+=g[i];
print(ans),puts("");
} return 0;
}
當時考場上沒打算為了這5分再去思考啊
不過正解的思想還是很不錯的
基於上面的思想,我們可以看到$$ans=\sum_^^ \frac=nlogn\)
現在還有乙個問題是怎麼求\(a,b\)的最長公共字首\(p\)和最長公共字尾\(s\)
首先可以看一下uoj35,他所求的是相鄰\(rank\)的lcp
我們假設\(h[i]=lcp\\)
可以得到對於任意的\(j\)和\(k\)(假設\(rank[j]),\(lcp\=min\\)
直接用st預處理,每次詢問都是\(o(1)\),對上述複雜度無影響
ps.注意,我的**使用sam來構造sa的,由於sam建立的時候會有新的節點產生,所以陣列需要開大一些,不然會gg
然後最後的乙份問題就是要進行區間加1的操作,直接差分即可,不要再往複雜度上加無謂的log
#include#include#include#include#include#include#include#include#include#include#define ll long long
using namespace std;
inline char nc()
return *p1++;}
inline void read(int &x)
inline void read(ll &x)
inline int read(char *s)
inline void read(char &x)
int wt,ss[19];
inline void print(int x)
}inline void print(ll x)
}int n,m,s,b[80010],c[80010],d[80010],f[80010],g[80010];
char sx[80010];
struct data
a[80010];
int sa[80010],rank[80010],r1[80010],r2[80010],rank,f1[80010][20],f2[80010][20];
void extend(int x,int p)
int np=a[p].letter[x];
if (a[np].len==a[p].len+1) a[q].fa=np;
else
}void insert(char x)
void dfs(int x)
void build()
rank=0;
dfs(1);
}ll query(int x,int y)
void swap(char *s)
x+=i,y+=i;
}} int f=0,g=0;
for (int i=1;i<=m;i++)
f+=f[i],f[i]=f,g+=g[i],g[i]=g;
ll ans=0;
for (int i=1;ians+=(ll)g[i]*f[i+1];
print(ans),puts("");
} return 0;
}
NOI2016 優秀的拆分
看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...
NOI2016 優秀的拆分
題目實際上要求我們求從每個點出發的aa串的數量 考慮點i的答案,發現如果字首i與字首j j i 的最長公共字尾 i j,那麼i點出發向前就存在乙個長度為i j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件 考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的...
NOI2016 優秀的拆分
點此看題 首先轉化問題,我們可以求出a i b i a i b i a i b i 即以i ii結束 開始的aaaa aa串的數量,這樣答案就可以表示為 a i b i 1 sum a i times b i 1 a i b i 1 求這兩個陣列,可以隔距離len lenle n設定乙個點,這樣乙個...