您的位置:首页 > 产品设计 > UI/UE

URAL 1183 Brackets Sequence DP 路径输出

2015-12-14 05:46 441 查看
题意:长度小于100的字符串s只由四种字符"()[]"组成,求以该串为子串的最短的合法串。合法串递归定义为:

(1)空串合法

(2)如果S合法,则(S)、[S]合法

(3)如果A、B合法,则AB合法

思路:

设dp[i][j]为s(i,j)变为合法串后,合法串的长度或需要添加的字符的个数,状态转移:

(1)如果s[i]和s[j]匹配,dp[i,j]=dp[i+1,j-1]。

(2)如果不匹配,划分s(i,j)为s(i,k)和s(k+1,j),划分后dp[i,j]=dp[i,k]+dp[k+1,j],如果两个子串匹配的括号数最多,那么需要添加的字符比较最少,即dp[i,k+1]+dp[k+1,j]最小,k类似分段点,s(i,k)和s(k+1,j)可以分别输出,所以dp[i,j]=min(dp[i,k]+dp[k+1,j])

依赖关系:

求dp[i,j]要先求[i,k][k,j],即大区间依赖于小区间,所以要从区间长度最小的开始遍历。要把小区间全部遍历完,就从0开始遍历起点。所以有

for[code] len =
2
to n

for
start =
0
to n-
1

  
状态转移,遍历
k


初始状态:

dp[i][i]一定需要匹配一个字符,所以dp[i,i]=1

输出

如果知道s中哪个是不能在s中匹配的,在该字符旁边加一个匹配的就可以了。s[i,j]需要dp[i,j]个字符匹配,分情况讨论情况:

如果i>j,非法区间

如果i==j,单个字符,直接输出对应的"()"或者"[]"

如果i<j,合法区间,分情况

如果s[i]和s[j]匹配,只需看s(i+1,j-1)看,递归输出 output(s[i]); output(i+1,j-1); output(j);

如果s[i]和s[j]匹配不匹配,在dp[i,k]在状态转移时分段点k是唯一的,(i,k)和(k+1,j)是独立的,所以可以用一个pos[i,j]记录下k,然后分段输出,即output(i,k); output(k+1,j)

算法步骤:

步骤1:初始化dp[][]为无穷大,dp[i][i]=1,pos=-1。

步骤2:递推求dp[i][j],然后根据pos递归输出新串。

代码:

int dp[110][110],pos[110][110];
void DFSprint(int l,int r)   // 路径输出
{
if(l>r) return ;
if(l==r){
if(s[l]=='('||s[l]==')') cout << "()" ;
else cout << "[]" ;
}
else if(pos[l][r]==-1){
cout << s[l]; DFSprint(l+1,r-1); cout << s[r];
}
else{
DFSprint(l,pos[l][r]);  DFSprint(pos[l][r]+1,r);
}
}
int main()
{
cin >> s ;
memset(dp,inf,sizeof(dp));
memset(pos,-1,sizeof(pos));
int len = s.size();

for(int i=0;i<len;++i)
dp[i][i]=1,dp[i+1][i]=0;

for(int length=1; length<len; ++lenght){
for(int start=0; start+length<len; ++start){
dp[i][j]=len+1; // max
// s[i] s[j] 匹配
if( (s[i]=='('&&s[j]==')') || (s[i]=='['&&s[j]==']'))
dp[i][j]=min(dp[i][j], dp[i+1][j-1]);
// 状态转移
for(int k=i;k<j;++k){
if(dp[i][j] > dp[i][k]+dp[k+1][j]){
dp[i][j] =  dp[i][k]+dp[k+1][j];
pos[i][j] = k;
}
}
}
}
DFSprint(0,len-1);
cout << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: