CF802I Fake News (hard) (后缀数组+单调栈)
CF802I Fake News (hard)
这个题和 CF123D 很像,代码只有一点点不同。
首先看到子串的问题容易想到后缀数组,所以我们可以先对字符串求一遍后缀数组以及 height
数组。
接下来怎么做?我们其实可以想得到单调栈。我们可以考虑对于 height
数组维护一个单调递增的栈。一旦我们要弹出栈顶元素时,我们就知道他一定会对答案做出新贡献,即 \(cnt(s,p)>1\)。我们只需要把这些有新贡献的串的个数算出来,然后把其他只出现过一次的串的数量统计出来,答案就算出来了。
时间复杂度 \(O(n\log n+n)\)。
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=1e5+10;
int n,m;
int sa[MAXN],rnk[MAXN],height[MAXN],tmp[MAXN],a[MAXN],b[MAXN],cnt[MAXN],calc[MAXN];
char s[MAXN];
void bucket_sort(int v[]) {
fill(cnt,cnt+m+1,0);
for(int i=1;i<=n;i++) {
cnt[v[i]+1]++;
}
for(int i=1;i<=m;i++) {
cnt[i]+=cnt[i-1];
}
for(int i=1;i<=n;i++) {
tmp[++cnt[v[sa[i]]]]=sa[i];
}
for(int i=1;i<=n;i++) {
sa[i]=tmp[i];
}
}
void get_sa() {
for(int i=1;i<=n;i++) {
rnk[i]=tmp[i]=s[i];
sa[i]=i;
}
sort(tmp+1,tmp+n+1);
m=unique(tmp+1,tmp+n+1)-tmp-1;
for(int i=1;i<=n;i++) {
rnk[i]=lower_bound(tmp+1,tmp+m+1,rnk[i])-tmp;
}
for(int l=1;l<n;l<<=1) {
for(int i=1;i<=n;i++) {
a[i]=rnk[i];
b[i]=(i+l<=n? rnk[i+l]:0);
}
bucket_sort(b);
bucket_sort(a);
m=0;
for(int i=1;i<=n;i++) {
if(a[sa[i]]!=a[sa[i-1]]||b[sa[i]]!=b[sa[i-1]]) {
m++;
}
rnk[sa[i]]=m;
}
}
}
void get_height() {
int h=0;
for(int i=1;i<=n;i++) {
if(h) {
h--;
}
if(rnk[i]==1) {
continue;
}
int p=i+h;
int q=sa[rnk[i]-1]+h;
while(p<=n&&q<=n&&s[p]==s[q]) {
p++;
q++;
h++;
}
height[rnk[i]]=h;
}
}
int solve() {
int ans=n*(n+1)/2;
stack<int> stk;
for(int i=2;i<=n+1;i++) {
while(!stk.empty()&&height[stk.top()]>height[i]) {
int y=height[stk.top()];
int x=height[i];
stk.pop();
int z=(stk.empty()? 0:height[stk.top()]);
int len=i-(stk.empty()? 1:stk.top());
ans+=(y-max(x,z))*(len*len-len);
}
stk.push(i);
}
return ans;
}
signed main() {
int t=read();
while(t--) {
scanf("%s",s+1);
n=strlen(s+1);
get_sa();
get_height();
printf("%lld\n",solve());
}
return 0;
}
对于单调栈的思考,我有一个小技巧,可以通过画柱形图来帮助思考,这样会很直观