这题很常规的思路就是类似找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吗?)
一点想法而已。。。