Once Petya read a problem about a bracket sequence. He gave it much thought but didn’t find a solution. Today you will face it.
You are given string s. It represents a correct bracket sequence. A correct bracket sequence is the sequence of opening (“(“) and closing (“)“) brackets, such that it is possible to obtain a correct mathematical expression from it, inserting numbers and operators between the brackets. For example, such sequences as “(())()” and “()” are correct bracket sequences and such sequences as “)()” and “(()” are not.
In a correct bracket sequence each bracket corresponds to the matching bracket (an opening bracket corresponds to the matching closing bracket and vice versa). For example, in a bracket sequence shown of the figure below, the third bracket corresponds to the matching sixth one and the fifth bracket corresponds to the fourth one.
You are allowed to color some brackets in the bracket sequence so as all three conditions are fulfilled:
- Each bracket is either not colored any color, or is colored red, or is colored blue.
- For any pair of matching brackets exactly one of them is colored. In other words, for any bracket the following is true: either it or the matching bracket that corresponds to it is colored.
- No two neighboring colored brackets have the same color.
Find the number of different ways to color the bracket sequence. The ways should meet the above-given conditions. Two ways of coloring are considered different if they differ in the color of at least one bracket. As the result can be quite large, print it modulo 1000000007 (109 + 7).
The first line contains the single string s (2 ≤ |s| ≤ 700) which represents a correct bracket sequence.
Print the only number — the number of ways to color the bracket sequence that meet the above given conditions modulo 1000000007 (109 + 7).
(())
12
(()())
40
()
4
Let’s consider the first sample test. The bracket sequence from the sample can be colored, for example, as is shown on two figures below.
The two ways of coloring shown below are incorrect.
题目大意:
给出一个的括号序列。保证序列中括号两辆匹配,并且唯一匹配。
有红蓝绿三种涂料,现要给括号染色,要求每对括号中有且只有一个括号被染色,任意相邻括号颜色不可相同,无色除外。
误打误撞走上正轨……
开始想的是dp,普通的遍历dp。
后来想用之前做括号匹配的方式,进栈出栈搞一下。
最后想到用搜索,递归的时候处理。
真是开始的思路影响很大啊,要是照着前两种想法,应该会在歧途上越走越远……
搜索的初步思路是,每找到一对括号时,以其左右边界为新边界,搜索括号内包含的括号。
那么搜索出什么可以让递归变得有意义呢?
对于当前边界,或者说对于括号[L,R]而言,可涂色的方案有2*3种,左涂三种颜色和右涂三种颜色。
相邻括号颜色不能相同,导致方案数受其内包含括号,以及其左右括号影响。
现不考虑左右相邻括号。其内包含括号可能有多个,类似
(()())
这种情况
而对于最外层括号2*3种情况的合法性,由且仅由内部最左和最右括号颜色的选择有关。可知有3*3种选择。
这样问题就进一步细化了,以外层括号为边界时,想要搜索到的答案其实就是内部最左最右两个括号3*3种组合各自的方案数。
如果内部包含的只有一个括号,类似 (()) 这种,或者 ((()())) 这种也算,因为我们在乎的只是当前层的下一层,其余的归下一层管。内部的组合就是通过搜索返回的东西。
如果是多个括号,类似 (()()) ,那么 dp[dep][i][j] 定义为dep层,最左括号为i色,最右括号为j色的方案数,这里用到类似转移的方法,当在当前层找到新括号时,搜索出这个括号2*3种涂色的方案数,然后与这个dp数组组合出来,更新到其中即可。
另外需要注意的是,这里求出的是当前层内部左右颜色组合的方案数,当要返回时,需要转换到上一层,也就是要把当前层3*2涂色的方案数求出。
最顶层注意不需转换,因为边界的两个括号是虚加上的
代码略拖拉略搓。。。慎看
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;
char str[777];
LL dp[777][3][3];
LL tmp[3][3];
void dfs(int l,int r,int dep)
{
//初始化当前层左右边界涂色方案
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
dp[dep][i][j] = ((i && j) || (i == 0 && j == 0))? 0: 1;
//内部没有多余括号
if(l >= r-1) return;
int st = l+1;//左括号位置
int ph = 0;//用来匹配完整括号 左括号++ 右括号-- 当变为0说明出现当前层+1层的括号匹配
bool q = 0;//当前层内部是否遍历过括号
for(int i = l+1; i < r; ++i)
{
if(str[i] == '(') ph++;
else ph--;
if(i != l+1 && (i == r || str[i] == ')') && ph == 0)
{
dfs(st,i,dep+1);
st = i+1;
if(q == 0)//边界内部出现的第一个完整匹配
{
for(int j = 0; j < 3; ++j)
for(int k = 0; k < 3; ++k)
dp[dep][j][k] = dp[dep+1][j][k];
}
else
{
for(int j = 0; j < 3; ++j)
for(int k = 0; k < 3; ++k)
{
tmp[j][k] = dp[dep][j][k];
dp[dep][j][k] = 0;
}
for(int j = 0; j < 3; ++j)
for(int k = 0; k < 3; ++k)
for(int ll = 0; ll < 3; ++ll)
for(int rr = 0; rr < 3; ++rr)
if(ll != rr || ll == 0)
dp[dep][j][k] = (dp[dep][j][k] + (tmp[j][ll]*dp[dep+1][rr][k])%mod)%mod;
}
q = 1;
}
}
if(dep)//不是最顶层(边界为真实的括号
{
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
{
tmp[i][j] = dp[dep][i][j];
dp[dep][i][j] = 0;
}
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
if((i == 0) + (j == 0) == 1) for(int ll = 0; ll < 3; ++ll)
if(i != ll || i == 0) for(int rr = 0; rr < 3; ++rr)
if(j != rr || j == 0) dp[dep][i][j] = (dp[dep][i][j] + tmp[ll][rr])%mod;
}
}
int main()
{
//fread("");
//fwrite("");
scanf("%s",str);
dfs(-1,strlen(str),0);
LL ans = 0;
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
ans = (ans+dp[0][i][j])%mod;
printf("%lld\n",ans);
return 0;
}