[BZOJ4560][JLOI2016]字符串覆盖(贪心+DP)

先用KMP求出所有可以放的位置,然后两个值分别处理。

最大值:

贪心,4!枚举放的先后位置顺序,2^3枚举相邻两个串是否有交。

若有交,则后一个的起始位置一定是离前一个的结束位置最近的位置,无交也一样。

最小值:

首先去掉被其它串包含的串,因为肯定可以和其它串放同样的位置。

将所有串从长到短排序方便DP。

f[S][i]表示当前放的串的情况为S,串目前所覆盖到的最后一个位置为i,覆盖的最小总长度是多少,则有:

当最后一个覆盖到i的串位置与其它串不相交时:f[S][i]=min{f[S'][k]}  这个用一个前缀和优化即可。

当相交时:f[S][i]=min(f[S'][j]+i-j) 因为串已经从长到短排好序了所有没有问题。这个用单调队列优化即可。

写的龟速,跑得飞快。

 1 #include
 2 #include
 3 #include
 4 #define mem(a) memset(a,0,sizeof(a))
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 using namespace std;
 7 
 8 const int N=10010;
 9 char s[5][N],A[N];
10 bool b[5],fl[5][N];
11 int T,n,len,L[5],pos[5][N],num[5],nxt[N],p[5],st[20],ed[20],f[20][N],g[20][N],q[20][N];
12 
13 void KMP(char S[],char T[],int pos[],int &num,bool fl[]){
14     int n=strlen(T+1),m=strlen(S+1),j=0; nxt[1]=0;
15     rep(i,2,m){
16         while (j && S[j+1]!=S[i]) j=nxt[j];
17         if (S[j+1]==S[i]) nxt[i]=++j; else nxt[i]=0; 
18     }
19     j=0; num=0;
20     rep(i,1,n){
21         while (j && S[j+1]!=T[i]) j=nxt[j];
22         if (S[j+1]==T[i]) j++;
23         if (j==m) pos[++num]=i-m+1,fl[i]=1,j=nxt[j];
24     }
25 }
26 
27 bool chk(char S[],char T[]){
28     int n=strlen(T+1),m=strlen(S+1);
29     rep(i,1,n-m+1){
30         bool flag=0;
31         rep(j,1,m) if (S[j]!=T[i+j-1]){ flag=1; break; }
32         if (!flag) return 1;
33     }
34     return 0;
35 }
36 
37 void solve_max(){
38     rep(i,1,n) p[i]=i; int ans=0; mem(fl);
39     do{
40         for (int S=0; S<1<<(n-1); S++){
41             int x=pos[p[1]][1]+L[p[1]]-1,res=L[p[1]]; ans=max(ans,res); bool flag=0;
42             rep(i,2,n){
43                 int d=upper_bound(pos[p[i]]+1,pos[p[i]]+num[p[i]]+1,x)-pos[p[i]];
44                 if (S&(1<<(i-2))){
45                      if (d<=num[p[i]] && pos[p[i]][d]>x) res+=L[p[i]],x=pos[p[i]][d]+L[p[i]]-1;
46                     else{ flag=1; break; }
47                 }else{
48                     d--;
49                     if (d && pos[p[i]][d]<=x) res+=L[p[i]]-(x-pos[p[i]][d]+1),x=pos[p[i]][d]+L[p[i]]-1;
50                     else{ flag=1; break; }
51                 }
52                 if (!flag) ans=max(ans,res);
53             }
54         }
55     }while (next_permutation(p+1,p+n+1));
56     printf("%d\n",ans);
57 }
58 
59 void solve_min(){
60     int S0=(1<1;
61     rep(i,1,n-1) rep(j,i+1,n) if (!b[j] && chk(s[j],s[i])) b[j]=1,S0^=1<<(j-1);
62     memset(f,0x3f,sizeof(f)); memset(g,0x3f,sizeof(g));
63     mem(f[0]); mem(g[0]);
64     rep(i,0,(1<1) st[i]=1,ed[i]=0;
65     rep(i,1,len){
66         for (int S=1; S<1<){
67             if (S&(S^S0)) continue; g[S][i]=g[S][i-1];
68             rep(j,1,n) if (S&(1<<(j-1)) && fl[j][i]){
69                 int S1=S^(1<<(j-1));
70                 if (i-L[j]>=0) f[S][i]=min(f[S][i],g[S1][i-L[j]]+L[j]);
71                 while (st[S1]<=ed[S1] && q[S1][st[S1]]<=i-L[j]) st[S1]++;
72                 if (st[S1]<=ed[S1]) f[S][i]=min(f[S][i],f[S1][q[S1][st[S1]]]+i-q[S1][st[S1]]);
73                 while (st[S]<=ed[S] && f[S][q[S][ed[S]]]-q[S][ed[S]]>=f[S][i]-i) ed[S]--;
74                 q[S][++ed[S]]=i; g[S][i]=min(g[S][i-1],f[S][i]);
75             }
76         }
77     }
78     printf("%d ",g[S0][len]);
79 }
80 
81 int main(){
82     freopen("bzoj4560.in","r",stdin);
83     freopen("bzoj4560.out","w",stdout);
84     for (scanf("%d",&T); T--; ){
85         mem(fl); mem(b); scanf("%s",A+1); len=strlen(A+1); scanf("%d",&n);
86         rep(i,1,n) scanf("%s",s[i]+1),L[i]=strlen(s[i]+1);
87         rep(i,1,n-1) rep(j,i+1,n) if (L[i]<L[j]) swap(s[i],s[j]),swap(L[i],L[j]);
88         rep(i,1,n) KMP(s[i],A,pos[i],num[i],fl[i]);
89         solve_min(); solve_max();
90     }
91     return 0;
92 }

 

转载于:https://www.cnblogs.com/HocRiser/p/9930778.html

你可能感兴趣的:([BZOJ4560][JLOI2016]字符串覆盖(贪心+DP))