codeforces 909 C. Python Indentation

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 ≤ N ≤ 5000) — the number of commands in the program. N lines of the program follow, each line describing a single command. Each command is either “f” (denoting “for statement”) or “s” (“simple statement”). It is guaranteed that the last line is a simple statement.

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。

分析:对每一行语句,其实只关心上一行语句的缩进是多少。为了更好的理解,可以先看一下暴力求所有方案的过程:
graph
如图,每一根红线表示一次搜索,最终一共有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]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值