FZU - 2218 Simple String Problem 状压dp

FZU - 2218 Simple String Problem

  题目大意:给一个长度为n含有k个不同字母的串,从中挑选出两个连续的子串,要求两个子串中含有不同的字符,问这样的两个子串长度乘积最大是多少?

   根据题目所给的k<=16很自然的想到用状压dp来处理,但不知道该dp个什么,在观摩大佬的做法后才明白。我们用0000000000000000到1111111111111111表示pomnlkjhgfedcba的字符存不存在的状态,某个字符存在的话对应位就是1,反之就是0。一开始dp[x]就表示,在给出的串中状态x的最长长度,这样很好的处理重复的字符了,比如像aab,abaab,ab,ba这些串,他们的状态都是3(也就是a位和b位是1其他全0),然后dp[3]就是5(abaab这个)。所以我们遍历一遍S的所有子串就可以得到所有状态在子串中的最长长度。

  因为是两个含有不同字符的子串长度相乘,假设目前k是4,然后一个子串是的状态是1001,那么和它含有不同字符的子串的状态分别是0010,0100,0110,也就是1001反状态0110的所有子状态,我们直接让dp[x]去和它所有反状态的子状态相乘的话就会有很多种组合,处理上就非常的麻烦,所以这时就有个优化,让dp[x]表示它和它子串中最长的长度,也就是1001,包含了0001,1000,1001这几种状态的情况,然后在判断答案时直接让dp[x]和它的反状态dp[(1<<k)-1-i](全1减去x)相乘就包含了它们各自子串的情况。具体的细节如代码。

  

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=(1<<16)+18;
 5 int dp[N];
 6 char s[2118];
 7 int main()
 8 {
 9     int n,k,t;
10     scanf("%d",&t);
11     while(t--)
12     {
13         scanf("%d%d",&n,&k);
14         scanf("%s",s);
15         for(int i=0;i<(1<<k);i++)
16             dp[i]=0;//初始化所有状况的初始最长长度 
17         for(int i=0;i<n;i++)//遍历所有子串的状态 
18         {
19             int x=0;
20             for(int j=i;j<n;j++)
21             {
22                 x|=1<<(s[j]-'a');//因为每个字符不管它在这个子串中重复出现了几次
23                 //只要出现了一次,对于位置就是1,所以直接对位与| 
24                 dp[x]=max(dp[x],j-i+1);
25             }
26         }
27         for(int i=0;i<(1<<k);i++)//让每个状态包含它的子状态 
28             for(int j=0;j<k;j++)
29                 if(i&(1<<j))//对位是1的异或取反 
30                     dp[i]=max(dp[i],dp[i^(1<<j)]);
31         //因为像10110包含了10100的状态,而10100已经包含了10000和00100
32         //所以只需要有1的位置对位异或,10110就能包含它所有子串的状态 
33         int ans=0;
34         for(int i=0;i<(1<<k);i++)
35             ans=max(ans,dp[i]*dp[(1<<k)-1-i]);//一个串的结果和它相反串的结果相乘 
36         printf("%d\n",ans);
37     }
38     return 0;
39 }
状压状压hashaki

 

转载于:https://www.cnblogs.com/LMCC1108/p/10523314.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值