1 条题解
-
0
自动搬运
来自洛谷,原作者为

Orion545
**搬运于
2025-08-24 21:44:41,当前版本为作者最后更新于2018-04-22 14:22:36,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
广告
蒟蒻のblog
思路
首先,有一个非常显然的思路就是dp:
设表示前i个字符,最后一个为j
然后发现这个东西有后效性
改!设代表前i个字符,最后15个的状态为j(压缩一下),转移的是候枚举增加那个字符,然后看从谁可以推过来
然后就TLE了,完全无压力
怎么优化这个算法?
显然,枚举完增加哪个字符以后,可以用AC自动机来实现多模匹配
然后发现:我们把j的定义变成AC自动机上面的点j,这样一个点就代表一种状态,状态之间互相不重复,而且也没有后效性
这样的定义方法还有一个好处:状态少,从个变成了个
于是我们得到最终做法:
最终算法
对于输入的模板串建立AC自动机
令表示前i个字符,最后一个字符跑到AC自动机的第j位上的最大答案
于是我们只要对于每个枚举下一个是A,B,C,转移到下一个节点j',然后跳一遍fail指针
对于匹配到的一个长度为k的模板串,
最后答案就是的最大值
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; struct node{ int num,fail,son[3]; node(){num=fail=0;memset(son,0,sizeof(son));} }a[510];int cnt; void add(char s[]){//插入字符串到trie int len=strlen(s),i,cur=0; for(i=0;i<len;i++){ if(!a[cur].son[s[i]-'A']) a[cur].son[s[i]-'A']=++cnt; cur=a[cur].son[s[i]-'A']; } a[cur].num++; } void getfail(){//bfs求fail指针 int u,v,q[510],head=0,tail=0,i; for(i=0;i<3;i++){ if(!a[0].son[i]) continue; a[0].fail=0;q[tail++]=a[0].son[i]; } while(head<tail){ u=q[head++]; for(i=0;i<3;i++){ v=a[u].son[i]; if(v) a[v].fail=a[a[u].fail].son[i],q[tail++]=v; else a[u].son[i]=a[a[u].fail].son[i]; } } } int m,n,dp[1010][510];bool vis[1010][510]; int proc(int cur,int val){//跳fail指针求值 while(cur) val+=a[cur].num,cur=a[cur].fail; return val; } int main(){ scanf("%d%d",&m,&n);int i,j,k,tmp;char s[20]; for(i=1;i<=m;i++) scanf("%s",s),add(s); getfail();vis[0][0]=1; for(i=0;i<n;i++){ for(j=0;j<=cnt;j++){ if(!vis[i][j]) continue; for(k=0;k<3;k++){ tmp=a[j].son[k]; dp[i+1][tmp]=max(dp[i+1][tmp],proc(tmp,dp[i][j])); vis[i+1][tmp]=1; } } } int ans=0; for(i=0;i<=cnt;i++) ans=max(ans,dp[n][i]); printf("%d\n",ans); }
- 1
信息
- ID
- 2105
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者