注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

时光机TimeMachine

——一个退役OIer

 
 
 

日志

 
 

[中南大学ACM1534]Not a subsequence  

2015-03-28 19:41:32|  分类: Problems |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

题意:
给你一个串a,求最短的串b使得不是a的子序列(不连续)
输出最小长度&方案数%(1e9+7)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1e6+1000;
const int MOD = 1E9+7;

int n,m;
int last[200];
int tmp0[N],tmp1[N];
int *f=tmp0+1;
int *dp=tmp1+1,mf[N],mdp[2];
int v[N];
char str[N];

int main(){
int i,j;
int nn;
scanf("%d",&nn);
while(nn--){
scanf("%d",&j);
f[0] = j;
n = 0;
scanf("%s",str+1);
n = strlen(str+1);
//clear1
for(i=1;i<=199;i++) last[i]=0;
//clear2
while(mdp[0]<=mdp[1]){
v[mdp[0]]=mf[mdp[0]]=0;
mdp[0]++;
}
mdp[0]=mdp[1]=0;

f[-1] = 1;
dp[-1]=0;
f[0]=j;
dp[0]=1;

mf[dp[-1]] = j*f[-1];
v[dp[-1]] = j;

for(i=1;i<=n;i++){
int l = last[str[i]];
//remove
mf[dp[l-1]]=(mf[dp[l-1]]-f[l-1])%MOD;
v[dp[l-1]]--;
//add
mf[dp[i-1]]=(mf[dp[i-1]]+f[i-1])%MOD;
v[dp[i-1]]++;

if(dp[i-1] > mdp[1])mdp[1] = dp[i-1];
while(mdp[0]<mdp[1] && v[mdp[0]]==0)mdp[0]++;
//while(mdp[0]<mdp[1] && v[mdp[1]]==0)mdp[1]--;

dp[i]=mdp[0]+1;
f[i]=mf[mdp[0]];
if(dp[i] > mdp[1])mdp[1] = dp[i];
last[str[i]] = i;

}
if(f[n]<0)f[n]+=MOD;
cout << dp[n] << ' '<<f[n] << endl;
}
return 0;
}

题解:
第一问:贪心
每次选择字符集中最后出现的字符,然后把a串的前面一部分丢掉继续做。
第二问:DP
dp[i]表示以i为前缀第一问的答案
f[i]表示方案数
然后我们从左往右扫,记录每个字符最后出现的位置last[i]
dp[i] = min(dp[last[ch]-1])+1
相当于在dp[last[ch]-1]这个不合法方案的最后加了一个字符ch
因为从i到last[ch]中ch只出现了一次,而dp[last[ch]-1]在前面部分不合法,所以在后面部分也没法匹配上。
转移的时候顺便统计一下f就OK了
然后因为每次枚举了放什么字符,所以是不重不漏的。
但是如果直接枚举所有字符就T了,我们注意到每次答案只+1,而且选择的是最小的一个。
所以更新过来的东西里只有两种取值……分别记录一下就好了
  评论这张
 
阅读(0)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017