【题目链接】 传送门
【题意】
给出一个长度为 n的字符串,请问长度为k的子序列,中本质不同的有多少?
比如abba,本质不同的长度为2的串有:ab,aa,bb,ba
【题解】
要做到不重不漏。需要借助序列自动机。
//创建序列自动机 , Next[i]['a'] -> 第 i 个位置 下一个'a' 的位置 for( int i = n ; i >= 1 ; i -- ){ for( int j = 0 ; j < 26 ; j ++ ) Next[i-1][j] = Next[i][j] ; Next[i-1][s[i]-'a'] = i ; }
利用序列自动机,把当前的位置的方案向后传送。
1、f[i][j],状态表示:表示以s[i]结尾,长度为j的子序列的方案数。
2、状态计算:f[i][j] = f[i][j] + f[k][j-1] ,k < i
为了方便计算,答案应该在当前位置,更新后面的。
f[0][0] = 1 ; for( int i = 0 ; i <= n ; i++ ){ for( int j = 0 ; j <= min( k , i ) ; j ++ ){ for( int c = 0 ; c < 26 ; c ++ ){ f[Next[i][c]][j+1] = ( f[i][j] + f[Next[i][c]][j+1] ) % mod ; } } }
【具体代码】
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 typedef long long ll ; 7 const ll mod = 1e9 + 7 ; 8 const int N = 1e3 + 10; 9 ll f[N][N] ; 10 char s[N]; 11 int Next[N][30] , n , k ; 12 int main() 13 { 14 scanf("%d%d",&n,&k); 15 scanf("%s",s+1); 16 17 //初始化 18 for( int i = 1 ; i <= 26 ; i++ ) 19 Next[n][i] = n + 1 ; 20 21 //创建序列自动机 , Next[i]['a'] -> 第 i 个位置 下一个'a' 的位置 22 for( int i = n ; i >= 1 ; i -- ){ 23 for( int j = 0 ; j < 26 ; j ++ ) 24 Next[i-1][j] = Next[i][j] ; 25 Next[i-1][s[i]-'a'] = i ; 26 } 27 //printf("%d %d %d ",Next[0][0],Next[0][1],Next[0][2]); 28 f[0][0] = 1 ; 29 for( int i = 0 ; i <= n ; i++ ){ 30 for( int j = 0 ; j <= min( k , i ) ; j ++ ){ 31 for( int c = 0 ; c < 26 ; c ++ ){ 32 f[Next[i][c]][j+1] = ( f[i][j] + f[Next[i][c]][j+1] ) % mod ; 33 } 34 } 35 } 36 ll ans = 0 ; 37 for( int i = k ; i <= n ; i++ ){ 38 ans = ( ans + f[i][k] ) % mod ; 39 } 40 printf("%lld ",ans); 41 return 0; 42 }