惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
H
Hacker News: Front Page
IT之家
IT之家
I
Intezer
GbyAI
GbyAI
MongoDB | Blog
MongoDB | Blog
博客园_首页
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
SegmentFault 最新的问题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Threatpost
Cisco Talos Blog
Cisco Talos Blog
C
Check Point Blog
P
Proofpoint News Feed
P
Privacy International News Feed
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
Scott Helme
Scott Helme
U
Unit 42
J
Java Code Geeks
W
WeLiveSecurity
H
Hackread – Cybersecurity News, Data Breaches, AI and More
C
CERT Recently Published Vulnerability Notes
小众软件
小众软件
The Hacker News
The Hacker News
L
LINUX DO - 热门话题
博客园 - 【当耐特】
G
Google Developers Blog
Latest news
Latest news
AWS News Blog
AWS News Blog
NISL@THU
NISL@THU
S
Secure Thoughts
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
F
Full Disclosure
S
Securelist
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Engineering at Meta
Engineering at Meta
Security Archives - TechRepublic
Security Archives - TechRepublic
人人都是产品经理
人人都是产品经理
T
Tor Project blog
Recent Announcements
Recent Announcements
Security Latest
Security Latest
N
News | PayPal Newsroom
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
T
The Blog of Author Tim Ferriss

博客园 - woodfish

偏序集的Dilworth定理 XJOJ历经2个月终于完成啦~~ fork()调用的一个趣题 实现google那种输入框提示的功能 哈尔滨赛区网络预选赛总结 [计算几何]点集中的点能组成多少个正方形 [计算几何]POJ 1375 点对圆的切线+线段重叠 [计算几何]POJ 1556 判断线段相交+Dijkstra [计算几何] POJ 1873 暴力+凸包 [计算几何]POJ 1266 三角形的外接圆 圆的参数方程 POJ 1026 置换群 [计算几何]POJ 1031 计算点对多边形的偏转角度 [计算几何]POJ2079 求点集中面积最大的三角形 [计算几何]POJ3608 求2个不相交凸包的最短距离 [计算几何]凸包的旋转卡壳算法 C++禁止一个类被继承的技术 C与汇编的接口技术 计算位数的3种方法 避免使用条件分支
POJ 3691 安徽第二题 有限状态自动机+DP
woodfish · 2008-10-03 · via 博客园 - woodfish

http://acm.pku.edu.cn/JudgeOnline/problem?id=3691

题目大意是说给定一个长度最大为1000的仅由A,T,C,G四个字符组成的字符串,要求改变最少的字符个数,使得得到的字符串中不含有任意预先给定的一系列子串。

 一般都会想到用动态规划,但是怎么DP是个问题,怎么设计状态。换个角度,我们要最终的字符串不含有任意模板串,这个涉及到多串匹配的知识。我们知道,可以建立一个有限状态自动机DFA来判断一个字符串中是否还有某些模板串。关于多模板串的DFA的建立,可以利用字符串前缀函数特殊的性质,建立一个TRIE图(具体建立方法可以参见相关论文)。这样,给定一个字符串,就可以在这个TRIE图中沿相应的边转移,如果途经一个危险节点,也就是目标串中出现了一个模板串。所以,给定一个模板串,如果在TRIE图中“走”的过程中没有到达过危险节点,那么这个字符串就符合要求。

现在来看原题,要求把一个字符串改变最小的字符数,使得最终的字符串符合要求。由于模板串不变,DFA也没有变化,所以我们这样DP:设dp[i][j]表示到了目标串的第i个字符,并且走到了自动机的状态点j,中途没有经过任何危险结点,所改变的字符串的最小个数。具体DP过程也是比较简单的。直接看代码就差不多了。最后的答案就是min{dp[len][j]} (len是目标串的长度,j是自动机中的一个状态点,并且状态j不是目标状态)

#include <stdio.h>
#include 
<string.h>const int maxn=1000;
const int maxnodes=480;
const int maxchar=4;int child[maxnodes][maxchar];
bool danger[maxnodes];
int suffix[maxnodes],q[maxnodes];
int nodes,f,r,p;
int dp[maxn][maxnodes];
char str[maxn];

inline 

int getwordid(char c) {
    
switch(c) {
        
case 'A':return 0;
        
case 'G':return 1;
        
case 'C':return 2;
        
case 'T':return 3;
    }
}
bool build_trie() {
    
char words[30];
    
int m;
    nodes
=1;
    scanf(
"%d",&m);
    
if(m==0return false;
    memset(child,
0,sizeof(child));
    memset(danger,
0,sizeof(danger));
    memset(suffix,
0,sizeof(suffix));
    memset(q,
0,sizeof(q));
    
for(int i=0;i<m;i++) {
        scanf(
"%s",words);
        
int len=strlen(words);
        p
=1;
        
for(int j=0;j<len;j++) {
            
int d=getwordid(words[j]);
            
if(child[p][d]==0) {
                nodes
++;
                child[p][d]
=nodes;
            }
            p
=child[p][d];
            
if(danger[p]) break;
        }
        danger[p]
=true;
    }
    
return true;
}
void build_graph() {
    f
=r=0;
    
for(int i=0;i<maxchar;i++) {
        
if(child[1][i]==0) {
            child[
1][i]=1;
        }
else {
            r
++;
            q[r]
=child[1][i];
            suffix[child[
1][i]]=1;
        }
    }
    
while(f<r) {
        f
++;
        danger[q[f]]
=danger[q[f]] || danger[suffix[q[f]]];
        
if(!danger[q[f]]) {
            
for(int i=0;i<maxchar;i++) {
                
if(child[q[f]][i]==0
                    child[q[f]][i]
=child[suffix[q[f]]][i];
                
else {
                    r
++;
                    q[r]
=child[q[f]][i];
                    suffix[q[r]]
=child[suffix[q[f]]][i];
                }
            }
        }
    }
}
void checkmin(int& a,int b) {
    
if(a==-1) a=b;
    
else if(a>b) a=b;
}
int main() {
    
//freopen("in.txt","r",stdin);
    int cases=0;
    
while(build_trie()) {
        cases
++;
        printf(
"Case %d: ",cases);
        build_graph();
        scanf(
"%s",str);
        
int len=strlen(str);
        
        memset(dp,
-1,sizeof(dp));
        
if(!danger[child[1][getwordid(str[0])]])
            dp[
0][child[1][getwordid(str[0])]]=0;
        
for(int i=0;i<maxchar;i++)
            
if(getwordid(str[0])!=i&&!danger[child[1][i]])
                dp[
0][child[1][i]]=1;
        
for(int i=1;i<len;i++) {
            
for(int j=1;j<=nodes;j++) {
                
if(dp[i-1][j]!=-1) {
                    
if(!danger[child[j][getwordid(str[i])]])
                        checkmin(dp[i][child[j][getwordid(str[i])]],dp[i
-1][j]);                 
                    
for(int k=0;k<maxchar;k++)
                        
if(getwordid(str[i])!=k&&!danger[child[j][k]])
                            checkmin(dp[i][child[j][k]],dp[i
-1][j]+1);
                }
            }
        }
        
int ans=1<<20;
        
for(int i=1;i<=nodes;i++)
                
if(!danger[i]&&dp[len-1][i]!=-1&&dp[len-1][i]<ans)
                    ans
=dp[len-1][i];
        
if(ans==1<<20) ans=-1;
        printf(
"%d\n",ans);
    }
    
return 0;
}