Description
In Python, code blocks don’t have explicit begin/end or curly braces to mark beginning and end of the block. Instead, code blocks are defined by indentation.
We will consider an extremely simplified subset of Python with only two types of statements.
Simple statements are written in a single line, one per line. An example of a simple statement is assignment.
For statements are compound statements: they contain one or several other statements. For statement consists of a header written in a separate line which starts with “for” prefix, and loop body. Loop body is a block of statements indented one level further than the header of the loop. Loop body can contain both types of statements. Loop body can’t be empty.
You are given a sequence of statements without indentation. Find the number of ways in which the statements can be indented to form a valid Python program.
Input
The first line contains a single integer
N
(1 ≤
Output
Output one line containing an integer - the number of ways the given sequence of statements can be indented modulo 10
9
+ 7.
Examples
Input |
---|
4 s f f s |
Output |
1 |
Input |
---|
4 f s f s |
Output |
2 |
Note
In the first test case, there is only one way to indent the program: the second for statement must be part of the body of the first one.
simple statement
for statement
for statement
simple statement
In the second test case, there are two ways to indent the program: the second for statement can either be part of the first one’s body or a separate statement following the first one.
for statement
simple statement
for statement
simple statement
or
for statement
simple statement
for statement
simple statement
感谢下面的博主的倾情分享~
https://www.cnblogs.com/widsom/p/8136805.html
http://www.cnblogs.com/Leohh/p/8135525.html
题意:f代表for语句,它后面的语句至少有一句是它内部的,其他语句要么在内部要么在外部,s语句是普通语句。给一个没有缩进的f, s代码,求一共有多少种满足上述的缩进方案,结果模1e9+7。
分析:对每一行语句,其实只关心上一行语句的缩进是多少。为了更好的理解,可以先看一下暴力求所有方案的过程:
如图,每一根红线表示一次搜索,最终一共有6+3+1种方案。可以发现,先后三次经过了(5, 0)到达(6, 0),先后2次经过(5, 1)分别到达(6, 0)和(6, 1),这些其实是完全相同的动作,可以利用动态规划避免这种重复的运算。其实就是记录了每个点被搜索的次数,然后把相同的搜索一次性转移到下一行上。假想把所有的搜索暂停在第5行,(5, 1)总共被搜索了两次,那么从它向下产生的搜索都是+2的。这样做,就把行与行直接转移的代价从接近斐波那契的复杂度降到了O(n^2)。这样的复杂度还是很高,但选择恰当的求和顺序最终可以降到O(n)。
思路:使用二维数组w[i][j]来记录第i行的字符在j缩进可以产生的搜索数
第i行是f,i+1行的状态更新:
w[i+1][j+1]+=w[i][j] 下一行一定是j+1缩进的
w[i+1][0~j] 不产生其他缩进
第i行是s,i+1行的状态更新:
w[i+1][j+1]+=w[i][j]
w[i+1][j] +=w[i][j-1~j]
w[i+1][j-1]+=w[i][j-2~j]
...
看似需要O(n^2)才能求和,但注意到求和的个数在累加,所以其实可以采用从右往左的更新方式,这样只需要更新sum,一遍就能更新整行:
w[i+1][j+1]+=w[i][j] (->sum)
w[i+1][j] +=w[i][j-1]+sum (->sum)
w[i+1][j-1]+=w[i][j-2]+sum (->sum)
...
终于把复杂度优化到O(n)了!代码虽然很短,但我看了一个下午才彻底理解其中的思想orz
需要注意的是,起始的时候w[0][0]的值应该设为1。
#include<stdio.h>
//#include<iostream>
//using std::cin;
//using std::cout;
#include<algorithm>
using std::sort;
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define mabs(x) ((x)>0?(x):(0-(x)))
#define N_max 5003
char ipt[N_max];
int n;
int w[N_max][N_max] = { 0 };
const int mod = 1000000007;
//非递归dp
void dp() {
for (int i = 0; i < n-1; ++i) {
if (ipt[i] == 'f')//来自f的增量
for (int j = 0; j <= i; ++j)
{
w[i + 1][j + 1]=(w[i+1][j+1]+ w[i][j])%mod;
}
//来自s的增量
else if (ipt[i] == 's'){
int sum = 0;
//从右往左更新
for (int j = i; j >= 0; --j) {
sum =(sum+ w[i][j])%mod;
w[i + 1][j] =(w[i+1][j]+ sum)%mod;
}
}
}
}
int main(){
char etr;
scanf("%d", &n);
scanf("%c", &etr);
for (int i = 0; i < n; ++i) {
scanf("%c%c", &ipt[i], &etr);
}
//起始量
w[0][0] = 1;
dp();
for (int t = 1; t < n; ++t) {//求和最后一行即是结果
w[n - 1][0] =(w[n-1][0]+ w[n - 1][t])%mod;
}
printf("%d\n",w[n-1][0]);
}