传送门:【codeforces】477C Dreamoon and Strings
题目分析:
看起来像是一个贪心,但其实是DP。
因为每一次操作(删除0个,删除1个,删除2个...等)都是独立的。
现在,我们设母串长度为n,子串长度为m。
设dp[i][j]表示母串匹配到第i个字符可以得到j个不重复的子串时所需要删除的最少的字符。
假设我们现在遍历到第i个字符,且这个字符与子串第一个字符匹配,首先假设从这个位置开始还能得到一个子串,设该串的结尾位置为ii,那么得到这个子串的代价为ii - i + 1 - m。
此时转移方程便是:
i > 0:dp[ii][j] = min { dp[ii][j] , dp[i - 1][j - 1] + ii - i + 1 - m | 1 <= j <= n } ;
i = 0:dp[ii][j] = ii - i + 1 - m ;
(如果从第i个位置开始匹配不到子串,则上面的转移便不需要了。)
然后还有一次转移:
i > 0:dp[i][j] = min { dp[i][j] , dp[i - 1][j] | 0 <= j <= n } ;
(这个转移从头到尾一直需要,除了i = 0时。)
这样答案基本就出来了。
扫一遍dp[n - 1][i],dp[n - 1][i]即删除dp[n - 1][i]个字符才能得到i个子串。
当达到最大以后,无用字符已经删除完毕,接下来再删除只能从子串中删除,易知每m个字符子串个数减一。
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std ; typedef long long LL ; #pragma comment(linker, "/STACK:1024000000") #define rep( i , a , b ) for ( int i = a ; i < b ; ++ i ) #define For( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define rev( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next ) #define clr( a , x ) memset ( a , x , sizeof a ) #define CPY( a , x ) memcpy ( a , x , sizeof a ) const int MAXN = 2005 ; const int INF = 0x3f3f3f3f ; char s[MAXN] ; char p[MAXN] ; int dp[MAXN][MAXN] ; int ans[MAXN] ; int n , m ; void solve () { n = strlen ( s ) ; m = strlen ( p ) ; clr ( dp , INF ) ; clr ( ans , 0 ) ; dp[0][0] = 0 ; int flag = 0 ; rep ( i , 0 , n ) { int ii = i , j = 0 ; if ( i ) For ( k , 0 , n ) dp[i][k] = min ( dp[i][k] , dp[i - 1][k] ) ; if ( !flag && s[ii] == p[j] ) { while ( ii < n && j < m ) { if ( s[ii] == p[j] ) ++ j ; ++ ii ; } if ( j < m ) { flag = 1 ; continue ; } -- ii ; int tmp = ii - i + 1 - m ; //printf ( "%d %d %d\n" , ii , i , m ) ; if ( i ) { For ( k , 1 , n ) { dp[ii][k] = min ( dp[ii][k] , dp[i - 1][k - 1] + tmp ) ; } } else dp[ii][1] = tmp ; } } int max_match = 0 ; For ( i , 1 , n ) { if ( dp[n - 1][i] != INF ) { max_match = i ; ans[dp[n - 1][i]] = i ; } else break ; } max_match *= m ; int x = ans[0] ; For ( i , 0 , n ) { if ( n - i < max_match ) printf ( "%d" , ( n - i ) / m ) ; else printf ( "%d" , x = max ( ans[i] , x ) ) ; if ( i < n ) printf ( " " ) ; else printf ( "\n" ) ; } } int main () { while ( ~scanf ( "%s%s" , s , p ) ) solve () ; return 0 ; }