這個算法主要是利用動態規劃的思想,給一個有n個字符的字符串s,和R條語法規則,則他的計算複雜度為\ord{n^3R},空間複雜度為\ord{n^2R}。類似於optimal binary search tree的算法,對於每個s[l,r]我們去計算s[l,k]和s[k+1,r]能符合的所有規則。
我們用2016 NCPC的題目當例子
這裡給一個簡單的範例,給你一個語法規則如下 :
A \Longrightarrow AAAAAAA \qquad 20 \\ A \Longrightarrow AA \qquad 15 \\ A \Longrightarrow a \qquad 5
如果看不懂的,他大約的意思如下:
一個合法字串A可以是七個合法字串A所組成,也可以是兩個合法字串A所組成,也可以是小寫字母a所組成。
每個規則都給他一個權重,表示經過這個規則的轉換會有這些花費,我們會給一個目標字串,要找出把整個字串轉換成A的最小花費,例如給定字串aaaaaaaa,他的最小花費就是75。
這裡舉另一個例子:
A \Longrightarrow BA \qquad 10 \\ A \Longrightarrow bcd \qquad 5 \\ B \Longrightarrow c \qquad 4
給定字串ccbcd,他的最小花費就是33。
當然也會發生無法轉換的情況,這個時候叫要輸出NIR 。
首先必須要把規則轉成喬姆斯基範式,這裡我以數字當作規則的名稱,當y=-1時表示這個cnf是s->x的規則,否則是s->xy的規則。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define MAXN 55 | |
struct CNF{ | |
int s,x,y;//s->xy | s->x, if y==-1 | |
int cost; | |
CNF(){} | |
CNF(int s,int x,int y,int c):s(s),x(x),y(y),cost(c){} | |
}; | |
int state;//規則數量 | |
map<char,int> rule;//每個字元對應到的規則,小寫字母為終端字符 | |
vector<CNF> cnf; | |
inline void init(){ | |
state=0; | |
rule.clear(); | |
cnf.clear(); | |
} | |
inline void add_to_cnf(char s,const string &p,int cost){ | |
if(rule.find(s)==rule.end())rule[s]=state++; | |
for(auto c:p)if(rule.find(c)==rule.end())rule[c]=state++; | |
if(p.size()==1){ | |
cnf.push_back(CNF(rule[s],rule[p[0]],-1,cost)); | |
}else{ | |
int left=rule[s]; | |
int sz=p.size(); | |
for(int i=0;i<sz-2;++i){ | |
cnf.push_back(CNF(left,rule[p[i]],state,0)); | |
left=state++; | |
} | |
cnf.push_back(CNF(left,rule[p[sz-2]],rule[p[sz-1]],cost)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
vector<long long> dp[MAXN][MAXN]; | |
vector<bool> neg_INF[MAXN][MAXN];//如果花費是負的可能會有無限小的情形 | |
inline void relax(int l,int r,const CNF &c,long long cost,bool neg_c=0){ | |
if(!neg_INF[l][r][c.s]&&(neg_INF[l][r][c.x]||cost<dp[l][r][c.s])){ | |
if(neg_c||neg_INF[l][r][c.x]){ | |
dp[l][r][c.s]=0; | |
neg_INF[l][r][c.s]=true; | |
}else dp[l][r][c.s]=cost; | |
} | |
} | |
inline void bellman(int l,int r,int n){ | |
for(int k=1;k<=state;++k) | |
for(auto c:cnf) | |
if(c.y==-1)relax(l,r,c,dp[l][r][c.x]+c.cost,k==n); | |
} | |
inline void cyk(const vector<int> &tok){ | |
for(int i=0;i<(int)tok.size();++i){ | |
for(int j=0;j<(int)tok.size();++j){ | |
dp[i][j]=vector<long long>(state+1,INT_MAX); | |
neg_INF[i][j]=vector<bool>(state+1,false); | |
} | |
dp[i][i][tok[i]]=0; | |
bellman(i,i,tok.size()); | |
} | |
for(int r=1;r<(int)tok.size();++r){ | |
for(int l=r-1;l>=0;--l){ | |
for(int k=l;k<r;++k) | |
for(auto c:cnf) | |
if(~c.y)relax(l,r,c,dp[l][k][c.x]+dp[k+1][r][c.y]+c.cost); | |
bellman(l,r,tok.size()); | |
} | |
} | |
} |
沒有留言:
張貼留言