【DP】操作集锦

【题目链接】  传送门

【题意】

  给出一个长度为 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\n",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\n",ans);
41     return 0;
42 }
View Code

猜你喜欢

转载自www.cnblogs.com/Osea/p/12603623.html