解题报告(二):uva 10029 - Edit Step Ladders(dp+hash)

     这题很常规的思路就是类似找LIS,但是又感觉很没办法二分,所以时间复杂度只能控制在O(n^2),还要对两个单词进行编辑距离处理,最坏运算量达到:25000*25000*16*16,就比较随机生成的字符串,也能达到25000*25000*8*8,总而言之,就是会超时

   所以要另想办法,于是我很无奈的看了看讨论版,发现很多人是用hash+dp过的,于是在想啊想:

  就有了以下思路:

  1.对所有单词的编辑距离后变化的单词进行hash处理

  2.对进行处理后的单词进行DAG建图

  3.对DAG找最长路

  具体实现见代码:

#include <iostream>

#include <cstdio>

#include <string>

#include <vector>

#include <cstring>

using namespace std;

#define LIM 25100

#define BIGHASH 4000001

//字符串结构体
class node{
public:
    node(const string& str,int id){
        this->str=str;
        this->id=id;
    }
    string str;
    int id;   //id 表示第几个单词
};

//单词个数
int wsize;

//储存单词数组
string word[LIM];


//hash表,每个单词变化后全部映射到此hash表中
vector<node> hashtab[BIGHASH+10];

//List[i]  表示第i个单词对应的变化单词列表
vector<string> List[LIM];

//构造的DAG图的邻接表
vector<int> G[LIM];

//在DAG图里面找最长路的数组
//path[i] 表示从i结点出发的最长路的长度
int path[LIM];

//经典hash函数,(unix内核的hash函数)
unsigned int hash(const string&str){
    unsigned long h=0;
    for(size_t i=0; i<str.size(); ++i){
        h=(h<<4)+str[i];
        unsigned long g=h & 0Xf0000000L;
        if( g ) h^= g>>24;
        h&=~g;
    }
    return h%BIGHASH;
}

//寻找最长路的数组初始化
void init(){
    memset(path,-1,sizeof(path));
}


void input(){
    wsize=0;
    string str;
    while( cin>>str ){
        if( wsize==0 || str != word[wsize-1] ){
            word[wsize++]=str;
        }
    }
}

// 对于替换,删除,增添的字符串进行hash,记录
void dohashtab(){
    char ch;
    string temp;
    for(int i=0;i<wsize;++i){
        //对于单词的每个字符进行替换操作(包括删除操作,此时把*当做空的)
        for(size_t j=0;j<word[i].size();++j){
            ch=word[i][j]; // 记录word[i][j]
            word[i][j]='*';
            //temp=word[i];
            List[i].push_back(word[i]); //将word[i][j] 替换后List[i]
            hashtab[hash(word[i])].push_back(node(word[i],i)); //插入hash结点
            word[i][j]=ch; //word[i][j] 换回来
        }
        //进行增添操作
        for(size_t j=0; j <= word[i].size() ; j++ ){
            temp=word[i];
            word[i].insert(j,1,'*'); //将字符的j处插入1个*
            List[i].push_back(word[i]);
            hashtab[hash(word[i])].push_back(node(word[i],i));
            word[i]=temp; // word[i] 换回来
        }
    }
}

//根据每个单词变化的找匹配的并且顺序在该单词后面的单词,建立一条边
//这样就能建立一个DAG了
void build(){
    for(int i=0;i<wsize;++i){
        for(vector<string>::iterator it=List[i].begin() ; it != List[i].end() ; ++it ){
            int t=hash(*it);
            for(vector<node>::iterator ithash=hashtab[t].begin(); ithash != hashtab[t].end(); ++ithash){
                if( ( (*ithash).id > i ) && ( (*it) == (*ithash).str) ){
                    G[i].push_back((*ithash).id);
                }
            }
        }
    }
}

//记忆化搜索找出最长路
int FindPath(int start){
    if( path[start] != -1){
        return path[start];
    }
    if( G[start].empty() ){
        return path[start]=1;
    }
    int ans=0;
    int temp;
    for(vector<int>::iterator it=G[start].begin(); it != G[start].end(); it++ ){
        if( (temp=FindPath(*it)) > ans ){
            ans=temp;
        }
    }
    return path[start]=ans+1;
}


void solve(){
    dohashtab();
    build();
    int temp;
    int ans=-1;
    /*for(int i=0;i<wsize;++i){
         for(vector<int>::iterator it=G[i].begin(); it != G[i].end(); it++ ){
             cout<<i<<" "<<*it<<endl;
         }
    }*/
    //从每个结点出发的最长路
    for(int i=0;i<wsize;++i){
        if( (temp=FindPath(i)) > ans){
            ans=temp;
        }
    }
    cout<<ans<<endl;
}





int main()
{
    //freopen("in.txt","r",stdin);
    init();
    input();
    solve();
    return 0;
}

后来觉的这种方法一个缺点,就是初始化很慢,(此题不需要初始化),如果此题是要多case,而不是只有一个case,我的程序初始化也许要占很多时间(其实就是hash表的初始化慢),不知道如果在函数内声明hash表会不会快点(会MLE吗?)

一点想法而已。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值