欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

UVA - 10029 Edit Step Ladders

程序员文章站 2024-02-24 19:41:58
...

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=970
题意:
对于两个单词x和y,如果x可以通过删除、添加或者修改一个字母得到y,那么称为一次转换。
给出一个按字典序排列的字符串序列,求其中一个子序列,这个子序列的每一项可以由前一项转换而来,且该子序列是按字典序排列的,求子序列的最大长度。(表达能力欠佳。。就酱紫看吧)

思路:通过分析样例可以看出,只要按顺序对每一个转换建一条有向边(方向:i–>j,其中i< j ),然后求最大深度就ok辣
问题的难点就变成了怎样建边。如果暴力对每个字符串建边,显然O(n^2)会TLE。然后我有考虑按照长度分组,len之差<=1才有建边的可能,不过最差的情况也还是n^2。那如果预处理出每个字符串可能会转换到的情况呢?一开始想到这觉得那需要保存很多状态,其实不然,我们不必关心每次转换的具体字母是什么,只需要关心是否能转换得到就行了。所以我们大可不必将所有具体的情况列出来。
重点来啦:我们可以对每个字符串处理出他可能转换到的情况,而需要转换的字母可以用通配符来代替。这样对于修改,最多只有16种情况。而删除和增加,我们可以只考虑增加,因为x删除一个字母得到y,和y增加一个字母得到x是等价的。这样对于每个字符串,最多处理16+17次。

最长路的求解
设dp[i]=到i点为止题目所求子序列的最大长度
对于第i个字符串,如果它前面有一个字符串j能转换到它,那么对于该点
dp[i]=max(dp[i],dp[j]+1)

好啦,所有的问题都解决了,然后贴代码:

#include <bits/stdc++.h>
#define N 25005
using namespace std;
vector<string>strnum;
map<string,int>mp;
int dp[N];

string strChange(string str,int x){
    string src;
    for(int i=0;i<str.size();i++){
        if(i!=x)
            src.push_back(str[i]);
        else
            src.push_back('*');
    }
    return src;
}
string strAdd(string str,int x){
    string src;
    if(x==str.size()) {
        src = str + '*';
        return src;
    }
    for(int i=0;i<str.size();i++){
        if(i==x)
            src.push_back('*');
        src.push_back(str[i]);
    }
    return src;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    string str;
    while(getline(cin,str)){
        if(str.size()==0)
            break;
        strnum.push_back(str);
    }
    memset(dp,0, sizeof(dp));
    int ans=0;
    for(int i=0;i<strnum.size();i++){
        for(int j=0;j<strnum[i].size();j++){
            string src=strChange(strnum[i],j);
            if(mp.count(src)>0)
                dp[i]=max(dp[i],dp[mp[src]]+1);
            mp[src]=i;
            src=strAdd(strnum[i],j);
            if(mp.count(src)>0)
                dp[i]=max(dp[i],dp[mp[src]]+1);
            mp[src]=i;
        }
        string src=strAdd(strnum[i],strnum[i].size());
        if(mp.count(src)>0)
            dp[i]=max(dp[i],dp[mp[src]]+1);
        mp[src]=i;
        ans=max(ans,dp[i]+1);
    }
    cout<<ans<<endl;
    return 0;
}
相关标签: 最长路 建边