问题
分析
参考:
https://blog.csdn.net/hyqsblog/article/details/46909863
https://github.com/aoapc-book/aoapc-bac2nd/blob/master/ch9/uva1375.cpp
https://blog.csdn.net/Fuxey/article/details/49279917
https://blog.csdn.net/u014258433/article/details/69070747
这道题感觉是这章到目前最抽象的一题,无论是思路还是解法都很难懂,我看了代码仓库中的代码,勉强看懂,但是下次估计还是做不出来,简单记载一下自己的理解
目标:求符合要求最小字典序的字符串
- 把上下文无关文法分解成基本的的结构A->BC这种形式,然后拆分后规则总数不超过maxn*(maxlen-1)个
- 定义状态d(i,L)是字符串i能够变成长度L的最小字典序字符串,i是字符串在字典中的序号
- 然后对于这些状态,将他们看作是一张图中的节点,状态之间的转移关系是由拆分后的规则决定的,这一步说起来简单,但是实际代码比较复杂
- 状态转移方程:分为两种转移方式,
不同层之间的:L进行拆分,
同层间的:L不变
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
using namespace std;
const int maxn=50+5; //rule的个数
const int maxlen=20+5; //name长度
const int maxs=50*20+100; //对于每一条规则,长度x,按照S=PA拆分,P是一个字母,A是字符串,能够产生x-1个规则
struct Transform{
int target,empty;
Transform(int t,int e):target(t),empty(e){}
};
int n,L,ns; //ns denotes number of sym
string rules[maxn];
string sym[maxs]; //id2sym
map<string,int> sym2id; //sym2id
string dp[maxs][maxlen]; //状态
vector<Transform> tr[maxs]; //记录转换过程 tr[i]表示序号为i的字符串可以 i+empty->target 或者 empty+i->target
int head[maxs],tail[maxs]; //存储S=PA拆分后的字符串
struct Node{
int x;
string s;
Node(int x,string s):x(x),s(s) {}
bool operator < (const Node &rhs) const {
return s>rhs.s; //为了在优先队列中从小到大排列
}
};
int ID(const string &s){
if(sym2id.count(s)) return sym2id[s];
sym[ns]=s;
return sym2id[s]=ns++;
}
//将规则进行拆分
void add_inter_symbol(const string &str){
int id=ID(str);
int len=str.length();
if(len<2) return;
int h=ID(str.substr(0,1));
int t=ID(str.substr(1));
tr[h].push_back(Transform(id,t));
tr[t].push_back(Transform(id,h));
head[id]=h; tail[id]=t;
}
//判断是否全部都是小数
bool isLowercase(const string &s){
for(int i=0;i<s.length();++i){
if(!(s[i]>='a' && s[i]<='z')) return false;
}
return true;
}
string min(const string &s1,const string &s2){
if(s1=="-") return s2;
return (s1<s2)?s1:s2;
}
bool vis[maxs];
//如果存在这样的规则 S->ij or S->ji,且dp[i][0]存在,那么就可以更新dp[id(S)][len]
void search(int len){
memset(vis,0,sizeof(vis));
priority_queue<Node>q;
for(int i=0;i<ns;i++) {
if(dp[i][len]!="-"){
q.push(Node(i,dp[i][len]));
}
}
while(!q.empty()) {
Node u=q.top();
q.pop();
int x=u.x;
string s=u.s;
if(vis[x]) continue;
vis[x]=true;
for(int i=0;i<tr[x].size();++i){
int target=tr[x][i].target;
int empty=tr[x][i].empty;
if(dp[empty][0]=="" && (dp[target][len]=="-" || s<dp[target][len])){
dp[target][len]=s;
q.push(Node(target,s));
}
}
}
}
int main(void){
while(cin>>n>>L && n){
sym2id.clear();
ns=0;
ID("");
for(int i=0;i<maxs;++i) tr[i].clear();
for(int i=0;i<n;++i){
cin>>rules[i];
int left=ID(rules[i].substr(0,1));
int right=ID(rules[i].substr(2));
// tr[left].push_back(Transform(right,ID("")));
tr[right].push_back(Transform(left,ID(""))); //S=AbC AbC can be transformed to S
int len=rules[i].length();
for(int k=2;k<len;++k){
add_inter_symbol(rules[i].substr(k));
}
}
//初始化,"-"代表不可达
for(int i=0;i<ns;++i){
for(int j=0;j<=L;++j){
dp[i][j]="-";
}
}
dp[0][0]=""; // dp[i][j] means the first string with len j that symbol i can be transformed into
for(int j=0;j<=L;++j){
for(int i=0;i<ns;++i){
if(sym[i].length()==j && (isLowercase(sym[i]))) dp[i][j]=sym[i];
if(sym[i].length()<2) continue;
//下面的步骤至少要有2个字母才能进行,不同层之间的状态转换
int s1=head[i],s2=tail[i];
for(int k=1;k<j;++k){
if(dp[s1][k]!="-" && dp[s2][j-k]!="-")
dp[i][j]=min(dp[i][j],dp[s1][k]+dp[s2][j-k]);
}
}
search(j); //同层之间的状态转换
}
cout<< dp[ID("S")][L] << "\n";
}
}