2020HDU多校第二场 1012.String Distance

题目大意:给了1个长度为n(1<=n<=1e5)的字符串A,和一个长度为m(1<=m<=20)的字符串B,有q(1<=q<=1e5)次询问,每次询问A[ l,...,r ]与B[ 1,...,m ]的“距离”。很显然,距离 = r - l + 1 - LCS (A[ l,...,r ] , B).

那么,我们现在的问题就转变成了如何快速地求得两字符串的LCS


 

题解思路:普通的LCS dp式是O(n*m)的,由于这题n很大,所以这种算法必然会超时,那么我们能否转换一下呢?我们注意到m很小,如果能只对B串内的元素进行dp,那么时间复杂度无疑会大大地降低。

朴素想法:开26个vector从小到大存下a串中每个元素的位置,dp[i][j] 代表的是在只考虑b[1,...,i]的情况下,拥有长度为 j 的LCS所需A串长度最小值(以 left 为起点),那么dp方程该如何转移呢?首先,假设我们已经知道 dp[i-1][j-1],如果我们需要将B[i]元素加入到LCS中,那么我们就需要在dp[i-1][j-1]这个位置的后面去寻找 B[i] 最小的位置p,然后 dp[i][j] = p。那么p怎么找呢?前面我们已经用vector处理出了每个元素的位置,二分查找一下即可,p=upper_bound ( G[B[i]-'a'].begin() , G[B[i]-'a'].end() ,dp[i-1][j-1] ),注意p不能超过右边界的范围。这样其实我们的dp转移就已经完成了。

然而,很尴尬地发现这样会TLE,因为dp是m2的,再加上个二分查找的log,就不可避免地 t 了

那么能不能把这个二分去掉呢?

优化想法:直接利用数组预处理出在[i,...,n]区间内每一个最先出现的字母的位置,g[i][j] 表示 A[i..n] 里字符 j 最早出现的下标。这种预处理操作叫做序列自动机。这样我们就不需要每次都去二分查找dp[i-1][j-1]这个位置的后面去寻找 B[i] 最小的位置p,这个位置p我们事先已经预处理出来了p=g[ dp[i-1][j-1] ][ B[i]-'a' ]。这样,算法就完成了。


 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
#define endl '\n'
#define p4 puts("444")
const int MAXN = 1e6+10;
const double Pi = acos(-1.0);
const double EPS = 1e-8;
const ll mod = 1e9+7;

int n,m;
int f[MAXN][26],dp[25][25];
char a[MAXN],b[MAXN];

void solve(){
    scanf("%s",a+1);n=strlen(a+1);
    scanf("%s",b+1);m=strlen(b+1);
    memset(f,0,sizeof(f));
    for(int i=0;i<26;i++)f[n][i]=1e9;
    for(int i=n;i>=1;i--){
        for(int j=0;j<26;j++)f[i-1][j]=f[i][j];
        f[i-1][a[i]-'a']=i;
    }
    int l,r,q;
    scanf("%d",&q);
    while(q--){
        scanf("%d %d",&l,&r);
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[0][0]=l-1;
        for(int i=1;i<=m;i++){
            dp[i][0]=l-1;
            for(int j=1;j<=i;j++){
                dp[i][j]=dp[i-1][j];
                if(dp[i-1][j-1]<=r)dp[i][j]=min(dp[i][j],f[dp[i-1][j-1]][b[i]-'a']);
            }
        }
        int ans=r-l+1+m;
        for(int i=m;i>=0;i--){
            if(dp[m][i]<=r&&dp[m][i]>=l){
                ans-=2*i;
                break;
            }
        }
        cout<<ans<<endl;
    }

}

int main()
{
    int T=1;
    scanf("%d",&T);
    while(T--)solve();
}

 

posted @ 2020-07-26 21:35  Mmasker  阅读(111)  评论(0编辑  收藏  举报