区间dp
题目大意:给出一个的只有’(‘,’)’,’[‘,’]’四种括号组成的字符串,求最多有多少个括号满足匹配。
题目链接
思路:用dp[i][j]表示区间i~j的最大匹配数,对于dp[i][j] = dp[i + 1][j - 1] + (s[i]和s[j]匹配?2 : 0),开始时dp[i][j]均为0。
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1e2 + 100;
char str[maxn];
int dp[maxn][maxn];
int main()
{
while(~scanf("%s", str) && strcmp(str, "end"))
{
memset(dp, 0, sizeof(dp));
int n = strlen(str);
for(int len = 2; len <= n; len++)
{
for(int i = 0; i < n; i++)
{
if(i + len - 1 > n) break;
int j = i + len - 1;
if((str[i] == '(' && str[j] == ')') || (str[i] == '[' && str[j] == ']'))
{
dp[i][j] = dp[i + 1][j - 1] + 2;
}
else dp[i][j] = dp[i + 1][j - 1];
for(int k = i; k < j; k++)
{
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
}
}
printf("%d\n", dp[0][n - 1]);
}
}
括号匹配升级:
问最少加多少括号,能使其完全匹配。加上个记录路径。
比如:( [ ( ] 最少加两个 变为 ( ) [ () ]
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1e2 + 100;
const int INF = 0x3f3f3f3f;
char str[maxn];
int dp[maxn][maxn], tag[maxn][maxn];
// tag[i][j]用于记录区间[i, j]在哪里断开
// dp[i][j]表示区间[i, j]最少的不匹配的数量。
void print(int l, int r) {
if(l > r) return ;
if(l == r) {
if(str[l] == '(' || str[r] == ')') printf("()");
else printf("[]");
return ;
}
if(tag[l][r] == -1) {
printf("%c", str[l]);
print(l + 1, r - 1);
printf("%c", str[r]);
}
else {
print(l, tag[l][r]);
print(tag[l][r] + 1, r);
}
}
int main()
{
while(~scanf("%s", str))
{
memset(dp, 0, sizeof(dp));
memset(tag, -1, sizeof(tag));
int n = strlen(str);
if(n == 0) {
printf("\n");
continue;
}
for(int i = 0; i <= n; i++) dp[i][i] = 1; //一个的时候是1
for(int len = 2; len <= n; len++)
{
for(int i = 0; i < n; i++)
{
if(i + len - 1 >= n) break;
int j = i + len - 1;
dp[i][j] = dp[i + 1][j - 1];
if((str[i] == '(' && str[j] == ')') || (str[i] == '[' && str[j] == ']'))
{ }
else dp[i][j] += 2;
//当str[i]与str[j]匹配时,dp[i][j] = dp[i + 1][j - 1]
//不匹配时,加2
for(int k = i; k < j; k++)
{
if(dp[i][j] >= dp[i][k] + dp[k + 1][j]) { //要加=号是因为']('这样的情况,在没进第三个for的时候就已经变成了2,[0,1]区间应该被分割开加符号,但是tag记录不到。
dp[i][j] = dp[i][k] + dp[k + 1][j];
tag[i][j] = k;
}
}
}
}
print(0, n - 1);
printf("\n");
}
}
附紫书上的做法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 100;
const int inf = 0x3f3f3f3f;
int dp[maxn][maxn], tag[maxn][maxn];
char str[maxn];
bool match(char a, char b) {
if((a == '(' && b == ')') || (a == '[' && b == ']')) return true;
return false;
}
void print(int l, int r) {
if(l > r) return ;
if(l == r) {
if(str[l] == '(' || str[r] == ')') printf("()");
else printf("[]");
return ;
}
if(tag[l][r] == -1) {
printf("%c", str[l]);
print(l + 1, r - 1);
printf("%c", str[r]);
}
else {
print(l, tag[l][r]);
print(tag[l][r] + 1, r);
}
}
int main()
{
gets(str);
int len = strlen(str);
if(len == 0) {
printf("\n");
return 0;
}
for(int i = 0; i < len; i++)
{
dp[i][i] = 1;
}
memset(tag, -1, sizeof(tag));
for(int i = len - 2; i >= 0; i--)
{
for(int j = i + 1; j < len; j++)
{
dp[i][j] = len + 1;
if(match(str[i], str[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];
tag[i][j] = k;
}
}
}
}
print(0, len - 1);
printf("\n");
}