题意:
给定一个母串S,和S的子串a,b,c,d。求子串能最多(最少)覆盖母串的多少字符?每个子串必须用上,且只能使用一次。
Sample:
hello
he
l
l
o
答案为:4 6
分析:
每个子串只能使用一次,且全部用上,集合状态,状态压缩。然后动态规划:
dp[i][j][k]表示S串里考虑到第i个字符,字符串集合状态为k(二进制状态压缩),从当前第i个字符已经向后覆盖了j个字符的最大(小)覆盖数。初始dp[i][0][0] = 0;
状态转移:对于当前状态dp[i][j][k],枚举每一个字符串p,其不在集合k中,如果它能和母串S从当前第i个字符开始匹配,那么考虑把它放在这里,状态转移到i+1阶段,dp[i+1][nj][cur],nj = max(j,len[p]),cur = k | (1<<p)。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 #include<algorithm> 5 #define maxn 5000 6 7 using namespace std; 8 char S[maxn]; 9 char str[70]; 10 int len[5]; 11 int match[maxn][5]; 12 int n; 13 int m; 14 bool check(int i) 15 { 16 int j = 1; 17 while (i<=n && j<=m) 18 { 19 if (S[i-1] != str[j-1]) 20 return false; 21 i++; 22 j++; 23 } 24 if (j==m+1) 25 return true; 26 return false; 27 } 28 void KMP(int k) 29 { 30 m = strlen(str); 31 len[k] = m; 32 for (int i=1;i<=n;i++){ 33 if (check(i)){ 34 match[i][k] = 1; 35 //printf("%d %d\n",i,k); 36 } 37 } 38 return ; 39 } 40 41 42 int dp[maxn][70][16]; 43 int DP_max() 44 { 45 int ans = 0; 46 memset(dp,-1,sizeof(dp)); 47 for (int i=1;i<=n;i++) 48 { 49 dp[i][0][0] = 0; 50 for (int j=0;j<=64;j++) 51 { 52 for (int k=0;k<=15;k++) 53 if (dp[i][j][k]>=0){ 54 int nj = j>0?j-1:0; 55 dp[i+1][nj][k] = max(dp[i+1][nj][k],dp[i][j][k]); 56 for (int p = 1;p<=4;p++){ 57 int tmp = 1<<(p-1); 58 if ((k&tmp)==0 && match[i][p]){ 59 nj = max(j,len[p]); 60 dp[i][nj][k|tmp] = max(dp[i][nj][k|tmp],dp[i][j][k] + nj - j); 61 } 62 } 63 } 64 ans = max(ans,dp[i][j][15]); 65 } 66 } 67 return ans; 68 } 69 int DP_min() 70 { 71 int inf = 1000000; 72 int ans = inf; 73 for (int i=1;i<=n;i++) 74 for (int j=0;j<=64;j++) 75 for (int k=0;k<=15;k++) 76 dp[i][j][k] = inf; 77 for (int i=1;i<=n;i++) 78 { 79 dp[i][0][0] = 0; 80 for (int j=0;j<=64;j++) 81 { 82 for (int k=0;k<=15;k++){ 83 if (dp[i][j][k] < inf){ 84 int nj = j>0?j-1:0; 85 86 dp[i+1][nj][k] = min(dp[i+1][nj][k],dp[i][j][k]); 87 for (int p=1;p<=4;p++){ 88 int tmp = 1<<(p-1); 89 if ((k&tmp)==0 && match[i][p]){ 90 nj = max(j,len[p]); 91 dp[i][nj][k|tmp] = min(dp[i][nj][k|tmp],dp[i][j][k]+nj-j); 92 } 93 } 94 } 95 } 96 ans = min(ans,dp[i][j][15]); 97 } 98 } 99 return ans; 100 } 101 int main() 102 { 103 while (scanf("%s",S)==1){ 104 n = strlen(S); 105 memset(match,0,sizeof(match)); 106 for (int i=1;i<=4;i++) 107 { 108 scanf("%s",str); 109 KMP(i); 110 } 111 112 int ans1 = DP_min(); 113 int ans2 = DP_max(); 114 115 printf("%d %d\n",ans1,ans2); 116 } 117 return 0; 118 }