ZJU1876 Edit Step Ladders - 动态规划 最长序列+二分优化

来源:互联网 发布:yum安装mongodb3.2 编辑:程序博客网 时间:2024/05/18 16:16

ZJU1876 PKU2564 Edit Step Ladders

题目大意:

如果单词A和单词B可以通过增加、删除、或修改一个字母而得到,并且A排在B之前,那么说A可以到达B。字典序给出n个单词(n<=25000),要求最长的可到达的单词串的长度。

分析:

这是很经典的最长非降子序列问题的变形,但由于单词和单词之间的比较没有偏序关系,所以不能使用O(nlogn)的LIS算法。用最直接的O(n^2)的算法可以求解。

设f[i]表示以第i个单词结尾的最长序列的长度。初始时f[0]=1。单词序列为a[i]

状态转移方程:f[i] = max{ f[j]+1 ,(0<=j<i, a[i]和a[j]可达) }

判断两个单词是否可达需要O(len(a[i]))的时间。

这样做不管怎么优化,最终还是TLE了……

这种最直接的做法没有充分利用到题目中的信息:“单词以字典序给出”。显然,这个性质可以用来二分查找。现在的问题是,有什么东西需要查找呢?

由于单词和单词之间可达只有三种变形方式,增加字母,删除字母,改变字母。如果单词a[i]要在前面i-1个单词中寻找一个可达的单词a[j],那么a[j]必然比a[i]字典序小。

所以我们可以考虑在计算f[i]时,枚举所有a[i]变形得到的单词s,然后在前i-1个单词中二分查找s是否存在。若a[j]==s那么a[i]可以接到a[j]后面。

这里有个必须加入的剪枝就是,枚举时按照字典序从小到大枚举s,当s大于a[i]时就退出当前枚举。因为按照题信息,a[i]之前的单词必定是字典序小于a[i]的。我尝试去掉这个剪枝,结果还是TLE了……

这样做在ZJU用时2.9s,在PKU要快一些。

查找单词s是否存在这个问题还可以用更高效的Trie Tree来实现。不是很熟……

花絮:

总是训练的时候做不出来,晚上睡觉的时候就能想到解法……

----------------------------------------------------------------------------------------------------------

/*
ZJU1876 Edit Step Ladders
*/

#include <stdio.h>
#include <string.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
 
#define N 25005
#define L 20

char a[N][L];
int f[N]={0},max;
char CH[]={'z','a','z'};


int bsearch(char a[][L],int n,char s[]){
    int l=0,r=n-1,mid,k;
    if(l>r) return -1;
    while(l<=r){
        mid=(l+r)/2;
        k=strcmp(a[mid],s);
        if(k==0) return mid;
        if(k>0) r=mid-1;
        if(k<0) l=mid+1;
    }
    return -1;
}

void add(char s[],int k,char ch,char t[]){
    int i;
    for(i=0;i<k;i++) t[i]=s[i];
    t[k]=ch;
    for(i=k;s[i];i++) t[i+1]=s[i];
    t[i+1]='/0';
}

void del(char s[],int k,char t[]){
    int i;
    for(i=0;i<k;i++) t[i]=s[i];
    for(i=k+1;s[i];i++) t[i-1]=s[i];
    t[i-1]='/0';
}

void change(char s[],int k,char ch,char t[]){
    int i;
    for(i=0;s[i];i++) t[i]=s[i];
    t[k]=ch;
    t[i]='/0';
}

void edit(int e,char s[],int k,char ch,char t[]){
    if(e==0) add(s,k,ch,t);
    if(e==1) del(s,k,t);
    if(e==2) change(s,k,ch,t);
}

int main()
{
    int i,j,k,m,n=0,e;
    char s[L],ch;

   
    //input
    while(scanf("%s",a[n])!=EOF) n++;

    //DP
    f[0]=1; max=1; c[0]=1;
    for(i=1;i<n;i++){
        f[i]=1;
        //edit words
        for(e=0;e<3;e++){   //0:add 1:del 2:change
            for(k=0;a[i][k];k++){
                for(ch='a';ch<=CH[e];ch++){
                    edit(e,a[i],k,ch,s);
                    if(strcmp(s,a[i])>=0) break;
                    j=bsearch(a,i,s);
                    if(j>=0&&f[i]<f[j]+1){
                        f[i]=f[j]+1;
                        if(max<f[i]) max=f[i];
                    }
                }
            }
        }
    }
   
    //output
    printf("%d/n",max);
    return 0;
}

 

原创粉丝点击