• 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 '
    '
    #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();
    }
  • 相关阅读:
    浏览器驱动
    django中的cookie和session
    django自定义中间件实现登陆
    django虚拟环境与文件上传
    了解和熟悉数据库相关知识
    JMeter ---相关脚本笔记
    JMeter脚本---关于时间戳的处理笔记
    JMeter中的读取json数据---JSON Extractor插件
    更多API知识学习
    认识VIM编辑器
  • 原文地址:https://www.cnblogs.com/Mmasker/p/13382165.html
Copyright © 2020-2023  润新知