题解:看上去就很像是DP题,实际上也的确是DP题。
首先考虑相对暴力的DP,用f[i][j]表示到第i个代码段为止缩进是j的方案数,那么转移就只要分类讨论一下,如果这一段是f那就是f[i][j]=f[i-1][j-1],f[i][0]=0,如果是s,那么可以随意把缩进往回拉,所以这一部分应该是,其中cnt表示目前已经累计的f的数量(也就是可能的最多的缩进数)。
这个写法的时间复杂度是,空间复杂度都是,考虑如何优化。空间方面,发现i只会由i-1转移而来,所以可以采用滚动数组来消去一个维度;时间方面,发现主要耗时的部分在于代码段为s的处理,而这一部分实际上是上一次的某一个后缀和,所以可以用一个新的数组s来维护当前的f数组的后缀和,这样每次用O(n)的效率维护,用O(n)的效率更新,就可以做到的时间复杂度了。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#define mod 1000000007
using namespace std;
int n;
long long ans=1,cnt,scnt,fcnt;
long long f[5010],s[5010];
char a[5010];
int main()
{
scanf("%d",&n);f[0]=1;s[0]=1;
for(int i=1;i<=n;i++)
{scanf("%c",&a[i]);while(a[i]<'a'||a[i]>'z') scanf("%c",&a[i]);if(a[i]=='s') scnt++;}
for(int i=1;i<=n;i++)
{
if(a[i]=='f')
{
for(int j=n;j>0;j--){f[j]=f[j-1];s[j]=s[j-1];}
f[0]=0;s[0]=s[1];fcnt++;
}
else
{
for(int j=0;j<=fcnt;j++) f[j]=(f[j]+s[j+1])%mod;
s[fcnt]=f[fcnt];
for(int j=fcnt-1;j>=0;j--) s[j]=(s[j]+s[j+1])%mod;
}
}
printf("%d\n",f[0]);
return 0;
}