Codeforces 1073G Yet Another LCP Problem $SA$+单调栈

本文介绍了一种使用后缀数组(SA)和单调栈解决字符串问题的方法,具体为求解给定字符串s和q个询问中,两个序列a和b的所有可能对的最长公共前缀(LCP)之和。通过预处理高度数组,每次询问转化为求区间最小值和,利用单调栈进行高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给出一个字符串\(s\)\(q\)个询问。
每次询问给出两个长度分别为\(k,l\)的序列\(a\)和序列\(b\)
\(\sum_{i=1}^{k}\sum_{j=1}^{l}lcp(s[a_i…n],s[b_j…n])\)

Solution

\(SA\)练习题。
求出\(height\)数组后,每次询问相当于询问\(l*k\)个区间\(min\)之和。
岂不单调栈?
对没错,这个题解就是提供给你代码对拍的

#include<bits/stdc++.h>
#define For(i,x,y) for (register int i=(x);i<=(y);i++)
#define Dow(i,x,y) for (register int i=(x);i>=(y);i--)
#define cross(i,k) for (register int i=first[k];i;i=last[i])
using namespace std;
typedef long long ll;
inline ll read(){
    ll x=0;int ch=getchar(),f=1;
    while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
    if (ch=='-'){f=-1;ch=getchar();}
    while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
const int N = 2e5+10;
int n,Q,l[N],k[N];
char s[N];
int SA[N],height[N],rk[N],cnt[N],x[N],y[N];
inline void RadixSort(){
    int Max=0;
    For(i,1,n) cnt[x[i]]++,Max=max(Max,x[i]);
    For(i,1,Max) cnt[i]+=cnt[i-1];
    Dow(i,n,1) SA[cnt[x[y[i]]]--]=y[i];
    For(i,1,Max) cnt[i]=0;
}
inline void GetSA(){
    For(i,1,n) x[i]=s[i],y[i]=i;
    RadixSort();
    for (int i=1,p;p<n;i<<=1){
        p=0;
        For(j,n-i+1,n) y[++p]=j;
        For(j,1,n) if (SA[j]>i) y[++p]=SA[j]-i;
        RadixSort(),swap(x,y),x[SA[1]]=p=1;
        For(j,2,n) x[SA[j]]=(y[SA[j]]==y[SA[j-1]]&&y[SA[j]+i]==y[SA[j-1]+i])?p:++p;
    }
    For(i,1,n) rk[SA[i]]=i;
    int now=0;
    For(i,1,n){
        if (rk[i]==1) continue;now=max(now-1,0);
        for (int j=SA[rk[i]-1];j+now<=n&&i+now<=n&&s[j+now]==s[i+now];now++);
        height[rk[i]]=now; 
    }
}
int Min[N][20],Log[N];
inline void init(){
    For(i,1,n) Log[i]=log(i)/log(2);
    For(i,1,n) Min[i][0]=height[i];
    For(j,1,Log[n]) For(i,1,n-(1<<j)+1) Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]);
}
inline int Query(int l,int r){
    int L=Log[r-l+1];
    return min(Min[l][L],Min[r-(1<<L)+1][L]);
}
struct node{
    int x,y;
}a[N<<1];
inline bool cmp(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;}
int top,q[N<<1],c[N<<1];
ll Sum,ans;
inline void solve(int m,int M){
    int cnt=0;
    For(i,1,M) a[++cnt]=(node){rk[read()],1};
    For(i,1,m) a[++cnt]=(node){rk[read()],0};
    sort(a+1,a+1+cnt,cmp),ans=top=Sum=0;
    a[cnt+1]=(node){-1,0};
    int r=cnt;while (a[r].y==1) r--;
    Dow(i,r,1){
        if (i!=r){
            int x=Query(a[i].x+1,a[i+1].x),s=a[i+1].y^1;
            while (x<=q[top]&&top) Sum-=c[top]*q[top],s+=c[top--];
            q[++top]=x,c[top]=s,Sum+=1ll*s*x;
        }
        if (a[i].y) ans+=Sum;
        else if (a[i+1].x==a[i].x) ans+=n-SA[a[i].x]+1;
    }
    For(i,2,cnt) if (a[i].x==a[i-1].x) swap(a[i],a[i-1]);
    top=Sum=0;
    r=1;while (a[r].y==1) r++;
    For(i,r,cnt){
        if (i!=r){
            int x=Query(a[i-1].x+1,a[i].x),s=a[i-1].y^1;
            while (x<=q[top]&&top) Sum-=c[top]*q[top],s+=c[top--];
            q[++top]=x,c[top]=s,Sum+=1ll*s*x;
        }
        if (a[i].y) ans+=Sum;
    }
}
ll Ans[N];
int main(){
    n=read(),Q=read(),scanf("%s",s+1);
    GetSA(),init();
    For(i,1,Q) solve(read(),read()),Ans[i]=ans;
    For(i,1,Q) printf("%lld\n",Ans[i]);
}

转载于:https://www.cnblogs.com/zykykyk/p/10121054.html

单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空栈。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将当前元素入栈。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的栈中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减栈。具体操作如下: 1. 创建一个空栈和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入栈。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减栈求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值