[编辑长度/字符串dp] HDOJ - 4323 Magic Number
http://acm.hdu.edu.cn/showproblem.php?pid=4323
关于编辑距离这篇博客讲得比较清楚, 还把第四种编辑"邻位交换"加进去了.
现在突然觉得原来字符串dp有好多,为什么?因为字符串是典型的无后效性吧,这种两串来二维dp的比如最长公共字串也是经典dp了.
状态描述:
// 状态: d[i][j] 表示 串a的 i 长子串(0~i-1), 变到 串b的 j 长子串 要最少花费多少操作(即a到b的编辑距离).
状态转移:
(la lb表示串长度.)
memset(d, 0, sizeof(d)); REP(la) d[i][0] = i; REP(lb) d[0][i] = i; FOR(i, 1, la) // 这里 i , j 表示 长度, 不是串下标!!(因为如果是下标的话, 0长度子串下标为-1无法表示, 除非用记忆化搜索解) FOR(j, 1, lb) { int del = d[i][j-1]+1; int ins = d[i-1][j]+1; int sub = d[i-1][j-1]+!(a[i-1] == b[j-1]); d[i][j] = min(min(del, ins), sub); }
问题的解:
return d[la][lb];
好像还有什么很厉害的BK树....以后再看吧...
用dp解,第一次交800+ms, 然后把串长调低点再交600+ms, 然后把min函数自己写inline, 484ms....还是挫...
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<cmath> #include<string> #include<vector> #include<map> #include<algorithm> using namespace std; int Rint() { int x; scanf("%d", &x); return x; } #define FOR(i, a, b) for(int i=(a); i<=(b); i++) #define FORD(i,a,b) for(int i=(a);i>=(b);i--) #define REP(x) for(int i=0; i<(x); i++) typedef long long int64; #define INF (1<<30) #define bug(s) cout<<#s<<"="<<s<<" " // 多决策dp, 字符串dp, 字符串编辑距离 // 状态: d[i][j] 表示 串a的 i 长子串(0~i-1), 变到 串b的 j 长子串 要最少花费多少操作(即a到b的编辑距离). #define MAXLEN 12 //串长 #define MAXN 1502 //目标串个数 #define MAXM 1002 //源串个数 int d[MAXLEN+1][MAXLEN+1]; //d[源][目标], 准确来讲下标应为 MAXLEN+1 int la, lb; //la源串长 int n, m; //目标串个数, 源串个数 char strb[MAXN][MAXLEN]; //目标串s char stra[MAXLEN]; //源串 int lim; // 阀值 inline int min(int a,int b) { return (a<b)?a:b; } int dp(char* a, char* b) { memset(d, 0, sizeof(d)); REP(la) d[i][0] = i; REP(lb) d[0][i] = i; FOR(i, 1, la) // 这里 i , j 表示 长度, 不是串下标!!(因为如果是下标的话, 0长度子串下标为-1无法表示, 除非用记忆化搜索解) FOR(j, 1, lb) { int del = d[i][j-1]+1; int ins = d[i-1][j]+1; int sub = d[i-1][j-1]+!(a[i-1] == b[j-1]); d[i][j] = min(min(del, ins), sub); } return d[la][lb]; } int main() { int T = Rint(); FOR(t, 1, T) { printf("Case #%d:\n", t); n = Rint(); m = Rint(); REP(n) { scanf("%s", strb[i]); } REP(m) { int ans = 0; scanf("%s%d", stra, &lim); la = strlen(stra); FOR(j, 0, n-1) { int done = 0; lb = strlen(strb[j]); if(abs(la-lb)>lim){} else { int cnt = dp(stra, strb[j]); done = cnt<=lim? 1: 0; } if(done) { //bug(strb[j])<<endl; ans++; } } printf("%d\n", ans); } } }