您的位置:首页 > 其它

4650: [Noi2016]优秀的拆分

2017-03-12 23:16 239 查看

4650: [Noi2016]优秀的拆分

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 192  Solved: 117

[Submit][Status][Discuss]

Description

如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆
分是优秀的。例如,对于字符串 aabaabaa,如果令 A=aabA=aab,B=aB=a,我们就找到了这个字符串拆分成 AABBA
ABB 的一种方式。一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=aA=a,B=baa
B=baa,也可以用 AABBAABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。现在给出一个长度为
 nn 的字符串 SS,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串
中连续的一段。

以下事项需要注意:

出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。在一个拆分中,允许出现
 A=BA=B。例如 cccc 存在拆分 A=B=cA=B=c。字符串本身也是它的一个子串。

Input

每个输入文件包含多组数据。输入文件的第一行只有一个整数 TT,表示数据的组数。保证 1≤T≤10。接
下来 TT 行,每行包含一个仅由英文小写字母构成的字符串 SS,意义如题所述。N<=≤30000

Output

输出 TT 行,每行包含一个整数,表示字符串 SS 所有子串的所有拆分中,总共有多少个是优秀的拆分。

Sample Input

4

aabbbb

cccccc

aabaabaabaa

bbaabaababaaba

Sample Output

3

5

4

7

explanation

我们用 S[i,j]S[i,j] 表示字符串 SS 第 ii 个字符到第 jj 个字符的子串(从 11 开始计数)。

第一组数据中,共有 33 个子串存在优秀的拆分:

S[1,4]=aabbS[1,4]=aabb,优秀的拆分为 A=aA=a,B=bB=b;

S[3,6]=bbbbS[3,6]=bbbb,优秀的拆分为 A=bA=b,B=bB=b;

S[1,6]=aabbbbS[1,6]=aabbbb,优秀的拆分为 A=aA=a,B=bbB=bb。

而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 33。

第二组数据中,有两类,总共 44 个子串存在优秀的拆分:

对于子串 S[1,4]=S[2,5]=S[3,6]=ccccS[1,4]=S[2,5]=S[3,6]=cccc,它们优秀的拆分相同,均为 A=cA=c,B=cB=c,但由于这些子串位置不同,因此要计算 33 次;

对于子串 S[1,6]=ccccccS[1,6]=cccccc,它优秀的拆分有 22 种:A=cA=c,B=ccB=cc 和 A=ccA=cc,B=cB=c,它们是相同子串的不同拆分,也都要计入答案。

所以第二组数据的答案是 3+2=53+2=5。

第三组数据中,S[1,8]S[1,8] 和 S[4,11]S[4,11] 各有 22 种优秀的拆分,其中 S[1,8]S[1,8] 是问题描述中的例子,所以答案是 2+2=42+2=4。

第四组数据中,S[1,4]S[1,4],S[6,11]S[6,11],S[7,12]S[7,12],S[2,11]S[2,11],S[1,8]S[1,8] 各有 11 种优秀的拆分,S[3,14]S[3,14] 有 22 种优秀的拆分,所以答案是 5+2=75+2=7。

HINT

Source



[Submit][Status][Discuss]

NOI2016D1T1...
其实这道题有95分部分分。。。而且相当好写(O(n^2logn))
写下正解做法。。。。
令f[i]:以i结尾的,形式如AA的字符串的组数
令g[i]:以i开头的,形式如AA的字符串的组数
若能预处理好f、g数组,那么答案就是∑f[i]g[i+1]
枚举字符串A的长度为L,在母串中每隔L个位置就设置一个关键点
由于AA的长度为2L,所以此时所有形式如AA的字符串都会经过且恰好经过两个关键点
对于原串的正串和反串都预处理出SA
每次枚举一个L,然后枚举相邻的两个关键点,求出它们的lcs和lcp
画出来大概这样


考虑相邻的两个关键位置x,y,lcs是从a,b开始的串
那么a+L-1和b+L-1是同构的,以及a+L和b+L是同构的...
讨论一下,就是一段连续的f和一段连续的g都加一
差分一下作前缀和即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 3E4 + 30;
const int T = 15;
typedef long long LL;

int n,TT,c[maxn],t1[maxn],t2[maxn],s1[maxn],s2[maxn],r1[maxn],r2[maxn]
,h1[maxn],h2[maxn],f[maxn],g[maxn],len[maxn],bin[maxn],m1[maxn][T],m2[maxn][T];
char A[maxn],B[maxn]; LL Ans;

void Work(int m,char *s,int *sa,int *rank,int *height)
{
memset(t1,0,sizeof(t1)); memset(t2,0,sizeof(t2));
memset(c,0,sizeof(c)); int *x = t1,*y = t2;
for (int i = 1; i <= n; i++) ++c[x[i] = s[i] - 'a' + 1];
for (int i = 2; i <= m; i++) c[i] += c[i - 1];
for (int i = 1; i <= n; i++) sa[c[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
for (int i = n; i > n - k; i--) y[++p] = i;
for (int i = 1; i <= n; i++) if (sa[i] - k > 0) y[++p] = sa[i] - k;
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) ++c[x[i]];
for (int i = 2; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i; i--) sa[c[x[y[i]]]--] = y[i];
swap(x,y); p = x[sa[1]] = 1;
for (int i = 2; i <= n; i++)
x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i] + k] == y[sa[i-1] + k] ? p : ++p;
m = p; if (m == n) break;
}

int k = 0;
for (int i = 1; i <= n; i++) rank[sa[i]] = i;
for (int i = 1; i <= n; i++)
{
if (k) --k;
int j = sa[rank[i] - 1];
while (s[i + k] == s[j + k]) ++k;
height[rank[i]] = k;
}
}

void Rmq_Pre()
{
for (int i = 1; i <= n; i++)
m1[i][0] = h1[i],m2[i][0] = h2[i];
for (int i = 1; i < T; i++)
for (int j = 1; j <= n; j++)
{
int Nex = j + (1 << i - 1);
if (Nex > n) break;
m1[j][i] = min(m1[j][i-1],m1[Nex][i-1]);
m2[j][i] = min(m2[j][i-1],m2[Nex][i-1]);
}
}

int lcs(int x,int y)
{
x = n - x + 1; y = n - y + 1;
x = r2[x]; y = r2[y]; if (x > y) swap(x,y);
++x; int k = y - x + 1;
return min(m2[x][bin[k]],m2[y - len[k] + 1][bin[k]]);
}

int lcp(int x,int y)
{
x = r1[x]; y = r1[y]; if (x > y) swap(x,y);
++x; int k = y - x + 1;
return min(m1[x][bin[k]],m1[y - len[k] + 1][bin[k]]);
}

void Solve()
{
scanf("%s",A + 1); n = strlen(A + 1);
for (int i = 1; i <= n; i++) B[i] = A[n - i + 1];
Work(26,A,s1,r1,h1); Work(26,B,s2,r2,h2); Rmq_Pre();
for (int L = 1; L <= n / 2; L++)
for (int i = L,j = i + L; i <= n; i += L,j += L)
{
int x = min(lcs(i,j),L),y = min(lcp(i,j),L);
if (x + y - 1 < L) continue;
++g[i - x + 1]; --g[i + y - L + 1];
++f[j - x + L]; --f[j + y];
}
for (int i = 1; i <= n; i++) f[i] += f[i - 1],g[i] += g[i - 1];
for (int i = 1; i < n; i++) Ans += 1LL * f[i] * g[i + 1];
cout << Ans << endl; Ans = 0;
memset(f,0,sizeof(f)); memset(g,0,sizeof(g));
memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2));
memset(r1,0,sizeof(r1)); memset(r2,0,sizeof(r2));
memset(h1,0,sizeof(h1)); memset(h2,0,sizeof(h2));
}

int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif

for (int i = 1; i < maxn; i++)
{
bin[i] = (1 << bin[i-1] + 1) < i ? bin[i-1] + 1 : bin[i-1];
len[i] = (1 << bin[i]);
}
cin >> TT; while (TT--) Solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: