链接:https://ac.nowcoder.com/acm/contest/625/K
来源:牛客网
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
众所周知,在算法竞赛中,出题人对他出的题的难度往往存在错误的估计。比如出题人本想出个简单题,没想到却出成了重坑细节题;本想出个中等难度题,结果却变成了防AK题。因此,为了让一场比赛能有良好的体验,有一个靠谱的验题人是非常重要的。
CC出好题目后,便拿给小马哥看。不出所料,这些题目小马哥全都是看一眼就会做,但他觉得这对于参赛选手来说还是有点难。为了避免CC被喷成毒瘤出题人,小马哥准备加一道签到题。
小马哥有一个只由小写字母组成的字符串S,他会问你一些关于这个字符串的问题。小马哥每次提问时会给你一个整数i,意思是要把字符串S在第i和第i+1个字符之间切断,这样便得到两个新串S[1,i]及S[i+1,|S|]。记A=S[1,i],B=S[i+1,|S|]。现在,先考虑A的所有连续子串,再考虑B的所有连续子串,小马哥问你A,B之间相同子串对有多少个。
也就是说,小马哥想让你计算下面这个式子:
ans=∑1≤l1≤r1≤|A|∑1≤l2≤r2≤|B|[ A[l1,⋯,r1]==B[l2,⋯,r2] ]ans=∑1≤l1≤r1≤|A|∑1≤l2≤r2≤|B|[ A[l1,⋯,r1]==B[l2,⋯,r2] ]
其中,求和式右边的式子是这样一个函数:
[x]={1(x=True)0(x=False)[x]={1(x=True)0(x=False)
你能解决小马哥的问题,并签到成功吗?
输入描述:
第一行是一个字符串S(1≤|S|≤1000),保证只由小写字母组成。
第二行是一个正整数T(1≤T≤105),表示询问次数。
接下来T行,每行一个整数i(1≤i≤n−1),表示一个询问。
输出描述:
对于每个询问,输出一行一个整数表示对应询问的ans,意义如题目描述中所述。
示例1
输入
复制
ababa
4
1
2
3
4
输出
复制
2
4
4
2
说明
对于i=3这个询问,原串被拆分成 A=aba,B=ba。
此时A的所有子串为:a,a,b,ab,ba,aba;B的所有子串为:a,b,ba。
因此A,B之间相同子串对为:(a,a),(a,a),(b,b),(ba,ba),共计4个。
题意:中文题略。
思路:
考虑n<=1000,先对字符串做一个n方的dp,求出以每个位置j与每个位置i为结尾的最长公共子串长度,再求出以每个位置i与每个位置j为开始的最长公共子串长度。
对于第一个字母,直接统计它在后面的字符串中出现的次数即可。
对于第i(i>1)个字母,答案就是1~i-1的答案,再加上所有j=i+1 ~ n,以i,j为结尾的最长公共子串长度(长度最大为j-i),再减去所有j=1~i-1中与以i为开始有最长公共子串的的答案(长度最大为i-j)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
char s[maxn];
int f[maxn][maxn],g[maxn][maxn];
int ans[maxn];
int n,q;
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) if (s[i]==s[j])
f[i][j]=f[i-1][j-1]+1;
for (int i=n;i>=1;i--)
for (int j=n;j>=1;j--) if (s[i]==s[j])
g[i][j]=g[i+1][j+1]+1;
for (int i=2;i<=n;i++) if (s[1]==s[i])
ans[1]++;
for (int i=2;i<=n;i++)
{
ans[i]=ans[i-1];
for (int j=i+1;j<=n;j++)
ans[i]+=min(f[i][j],j-i);
for (int j=1;j<i;j++)
ans[i]-=min(g[i][j],i-j);
}
scanf("%d",&q);
while (q--)
{
int x;
scanf("%d",&x);
printf("%d\n",ans[x]);
}
return 0;
}
而我比赛的时候依然蠢得没想到这个做法。
参考POJ 3415 Common Substrings的后缀数组做法,本题是其k=1的特例,并且要手动拆字符串。。。
由于后缀数组的解法是O(n)的,所以只需要暴力枚举前串,再套板子就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
const int maxn = 1e5+10;
int n,lena;
char a[maxn],aa[maxn];
typedef pair<char,int> pii;
int A[maxn],B[maxn];
int realrank[maxn],k,Len;
pii st[maxn];
int s[maxn][2];
long long h[maxn];
int K;
int C[maxn],D[maxn];
ll anss[maxn];
void init(int start){
rep(i,1,start)
a[i]=aa[i];
lena = start;
a[lena+1] = '#';
rep(i,start+1,Len)
a[i+1]=aa[i];
n = Len+1;
//cout << n<<a+1<<endl;
for(int i = 1; i <= n ; i++){
st[i] = make_pair(a[i],i);
}
sort(st+1,st+1+n);
k = realrank[st[1].second] = 1;
for(int i = 2; i <= n ; i++){
if(st[i].first != st[i-1].first)
k +=1;
realrank[st[i].second] = k;
}
}
void suffix_array(){
for(int i = 1; i <= n ; i *= 2){
for(int j = 0 ; j <= n ; j++)
A[j] = B[j] = 0;
for(int j = 1; j <= n ; j++){
A[s[j][0] = realrank[j]]++;
if(j+i <= n)
s[j][1] = realrank[j+i];
else s[j][1] = 0;
B[s[j][1]]++;
}
for(int j = 1; j <= n ;j++)
A[j] += A[j-1],B[j] += B[j-1];
for(int j = n ; j >= 1; j--){
C[B[s[j][1]]] = j;
B[s[j][1]]--;
}
for(int j = n ; j >=1; j--){
D[A[s[C[j]][0]]] = C[j];
A[s[C[j]][0]]--;
}
k = realrank[D[1]] = 1;
for(int j = 2 ; j <= n ; j++){
if(s[D[j]][0] != s[D[j-1]][0] ||s[D[j]][1] != s[D[j-1]][1])
k++;
realrank[D[j]] = k;
}
}
}
void gethigh(){
int pre = 0,j;
for(int i = 1; i <= n ; i++){
if(pre) pre--;
j = D[realrank[i]-1];
while(i+pre <= n && j + pre <= n && a[i+pre] == a[j+pre])
pre++;
h[realrank[i]] = pre;
}
}
struct node{
long long cnt,height;
}stka[maxn],stkb[maxn];
long long ans ,tot;
int topa ,topb,taila,tailb;
ll sov(){
//cout <<"len = "<<lena<<" "<<n<<endl;
ans = topa = topb = taila = tailb = tot = 0;
for(int i = 2; i <= n ; i++){
// printf("h[%d] = %d\n",i,h[i]);
if(h[i] < K){
topa = topb = taila = tailb = 0;
tot = 0;
}
else{
int num = 0;
if(D[i-1] <= lena){
num++;
tot += h[i] - K +1;
// cout <<"i = "<<i<<" to "<<tot<<endl;
}
while(taila > topa && stka[taila-1].height >= h[i]){
// cout <<"i = "<<i<<endl;
taila--;
tot -= stka[taila].cnt*(stka[taila].height-h[i]);
num += stka[taila].cnt;
// cout << " tot = "<<tot <<endl;
}
stka[taila].height = h[i];
stka[taila].cnt = num;
taila++;
if(D[i] > lena){
ans += tot;
}
}
}
topa = topb = taila = tailb = tot = 0;
for(int i = 2; i <= n ; i++){
if(h[i] < K){
topa = topb = taila = tailb = 0;
tot = 0;
}
else{
int num = 0;
if(D[i-1] > lena+1){
//printf("D[%d] = %d\n",i,D[i]);
num++;
tot += h[i] - K +1;
}
while(taila > topa && stka[taila-1].height >= h[i]){
taila--;
tot -= stka[taila].cnt*(stka[taila].height-h[i]);
num += stka[taila].cnt;
}
//cout <<"i = "<<i<<" stka[taila].height = "<<h[i]<<" stka[taila].cnt = "<<num<<endl;
stka[taila].height = h[i];
stka[taila].cnt = num;
taila++;
if(D[i] <= lena){
ans += tot;
}
}
}
return ans;
}
int main(){
K=1;
int T;
while(~scanf("%s",aa+1)){
memset(anss,0,sizeof(anss));
Len=strlen(aa+1);
rep(i,1,Len-1)
{
init(i);
suffix_array();
gethigh();
anss[i]=sov();
//anss[i]>>=1;
//cout<<anss[i]<<endl;
}
scanf("%d",&T);
while(T--)
{
int x;
scanf("%d",&x);
printf("%lld\n",anss[x]);
}
}
return 0;
}