POJ2774 --后缀树解法
POJ2774 Long Long Message --后缀树解法
题意明确说明求两字符串的最长连续公共子串,可用字符串hash或者后缀数据结构来做
关于后缀树
后缀树的原理较为简单,但 \(o(n)\) 的构建算法(Ukkonen算法)稍难理解,可参考以下博文
https://www.cnblogs.com/xubenben/p/3484988.html
- 在此也特别感谢该作者,本人也参考了上述文章作者的讲解,可以从我后面的代码看出和作者的代码步骤是一样的。我的代码主要体现的是对本题的dfs阶段的处理
思路
1.获得两个字符串ss1,ss2之后,将其拼接为\(ss1\) + "{" + \(ss1\) + "|",之所以选择这两个字符是因为其ascii码紧跟在'z'之后,对存储空间较为友好
2.对合串建立后缀树
3.遍历后缀树,记录经过的字符串长度,对找到一个经过的长度最长的非叶子节点,这个节点要同时满足:
- 有一个子树中包含{(当然这样的话必然包含|),说明这个节点属于ss1
- 有一个子树中包含|(并且不包含{),说明这个节点属于ss2
同时满足,说明从根到此节点的路程,经过的全是公共子串,可以根据记录的字符串长度更新答案
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int maxn = (1 << 30);
const int root = 1;
char ss[200010] = {0};
char ss2[100005] = {0};
int act = 1, co = 1;
int acteg = -1;
int tep = 0;
int ind = 0, rem = 0, s_end = -1;
int links[100005] = {0};
int vv[100005] = {0};
int mm = 0;
int linkk = 0;
int len1 = 0, len2 = 0;
int ans = 0; //本题答案
struct ab
{
int l;
int r;
int nex;
int alp[28]; // 后面26 27 下标代表的字符是 ‘{’和 ‘|’
} tree[1000005]; // 作为分割与结束符 (ascii相邻防止越界)
int add_new(int o, int ll = s_end, int rr = maxn)
{
tree[o].l = ll;
tree[o].r = rr;
return o;
}
void add_link(int o)
{
if (linkk)
{
tree[linkk].nex = o;
}
linkk = o;
}
int check_len(int o)
{
return min(tree[o].r, s_end) - tree[o].l + 1;
}
bool check_contain(int o)
{
int node_len = check_len(o);
if (node_len <= ind)
{
ind -= node_len;
tep += node_len;
act = o;
return true;
}
return false;
}
void add(char cc)
{
++rem;
linkk = 0;
while (rem > 0)
{
if (!ind)
{
tep = s_end;
}
int& actedge_node = tree[act].alp[ss[tep] - 'a'];
if (!actedge_node)
{
actedge_node = add_new(++co, s_end);
add_link(act);
}
else
{
if (check_contain(actedge_node))
{
continue;
}
else
{
if (ss[tree[actedge_node].l + ind] != cc) // 分裂注意原树(actedge_node)必须成为子树(否则会和原先的子树失去联系)
{
int leaf1 = add_new(++co, s_end);
int leaf2 = actedge_node;
int newtree = add_new(++co, tree[actedge_node].l, tree[actedge_node].l + ind - 1);
tree[newtree].alp[cc - 'a'] = leaf1;
tree[newtree].alp[ss[tree[actedge_node].l + ind] - 'a'] = leaf2;
tree[leaf2].l += ind;
actedge_node = newtree;
add_link(actedge_node);
}
else
{
++ind; // 活跃半径只在此处增加 ,增加完就加链并结束本次增点
// if (act != root)
// {
add_link(act);
// }
break;
}
}
}
--rem;
if (act == root)
{
if (!ind)
{
break;
}
tep = s_end - rem + 1;
--ind;
}
else
{
// ind = rem - 1;
// tep = s_end - rem + 1;
if (tree[act].nex)
{
act = tree[act].nex;
}
else
{
act = root;
}
}
}
}
int dfs(int o, int cc) // 本题所需的搜索 返回1代表包含{,2代表包含|,3代表都有
{
bool bk1 = false;
bool bk2 = false;
bool stop = false;
for (int i = 0; i <= 27; ++i)
{
if (tree[o].alp[i])
{
if (tree[tree[o].alp[i]].r != maxn)
{
int contain_terminal = dfs(tree[o].alp[i], cc + check_len(tree[o].alp[i]));
if (contain_terminal == 1)
{
bk1 = true;
}
if (contain_terminal == 2)
{
bk2 = true;
}
if (contain_terminal == 3)
{
bk1 = bk2 = true;
stop = true;
}
}
else
{
if (tree[tree[o].alp[i]].l > len1)
{
bk2 = true;
}
else
{
bk1 = true;
}
}
}
}
if (stop)
{
return 3;
}
if (bk1 && bk2)
{
ans = max(ans, cc);
return 3;
}
if (bk1)
{
return 1;
}
if (bk2)
{
return 2;
}
}
int main()
{
scanf("%s%s", ss, ss2);
len1 = strlen(ss);
len2 = strlen(ss2);
ss[len1] = '{'; //ss1的结束符,防止两字符串后缀拼接
for (int i = len1 + 1; i <= len1 + len2; ++i)
{
ss[i] = ss2[i - len1 - 1];
}
ss[len1 + len2 + 1] = '|'; //ss2的结束符(也是整个合串的结束符)
for (int i = 0; i <= len1 + len2 + 1; ++i)
{
++s_end;
add(ss[i]);
}
dfs(root, 0);
printf("%d", ans);
return 0;
}
POJ2774 --后缀树解法的更多相关文章
- 从Trie树(字典树)谈到后缀树
转:http://blog.csdn.net/v_july_v/article/details/6897097 引言 常关注本blog的读者朋友想必看过此篇文章:从B树.B+树.B*树谈到R 树,这次 ...
- [算法]从Trie树(字典树)谈到后缀树
我是好文章的搬运工,原文来自博客园,博主July_,地址:http://www.cnblogs.com/v-July-v/archive/2011/10/22/2316412.html 从Trie树( ...
- 笔试算法题(40):后缀数组 & 后缀树(Suffix Array & Suffix Tree)
议题:后缀数组(Suffix Array) 分析: 后缀树和后缀数组都是处理字符串的有效工具,前者较为常见,但后者更容易编程实现,空间耗用更少:后缀数组可用于解决最长公共子串问题,多模式匹配问题,最长 ...
- BZOJ 3238: [Ahoi2013]差异((单调栈+后缀数组)/(后缀树))
[传送门[(https://www.lydsy.com/JudgeOnline/problem.php?id=3238) 解题思路 首先原式可以把\(len\)那部分直接算出来,然后通过后缀数组求\( ...
- 后缀树(suffix tree)
参考: 从前缀树谈到后缀树 后缀树 Suffix Tree-后缀树 字典树(trie树).后缀树 一.前缀树 简述:又名单词查找树,tries树,一种多路树形结构,常用来操作字符串(但不限于字符串), ...
- 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组
涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...
- 后缀树系列一:概念以及实现原理( the Ukkonen algorithm)
首先说明一下后缀树系列一共会有三篇文章,本文先介绍基本概念以及如何线性时间内构件后缀树,第二篇文章会详细介绍怎么实现后缀树(包含实现代码),第三篇会着重谈一谈后缀树的应用. 本文分为三个部分, 首先介 ...
- 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树
另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...
- [转载]字典树(trie树)、后缀树
(1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边 ...
随机推荐
- linux 系统文件目录颜色及特殊权限对应的颜色
什么决定文件目录的颜色和背景? 颜色 说明 栗子 权限 白色 表示普通文件 蓝色 表示目录 绿色 表示可执行文件 浅蓝色 链接文件 黄色 表示设备文件 红色 表示压缩文件 红色闪烁 ...
- DataFrame索引和切片
import numpy as np import pandas as pd from pandas import DataFrame, Seriesdf = DataFrame(data=np.ra ...
- JavaScript 格式化数字、金额、千分位、保留几位小数、舍入舍去…
JavaScript 格式化数字.金额.千分位.保留几位小数.舍入舍去… 类库推荐 1. Numeral.js 一个用于格式化和操作数字的JavaScript库.数字可以被格式化为货币,百分比,时间, ...
- MFC 添加C++类,别的类不通过C++类的定义的对象就可以直接调用C++类里面的成员函数;
MFC 添加C++类,不用定义C++类的对象,别的类不通过C++类的定义的对象就可以直接调用C++类里面的成员函数: 1先在mfc程序中添加普通类CProdata,然后删除头文件Prodata.h里面 ...
- 通过网络经过路由器映射电脑ip到外网路由器
一.内网自己电脑做服务器 自己电脑ip为192.168.0.104 首先先把自己路由器ip192.168.1.82映射到外网路由器,其中外网路由器ip为59.40.0.196 让后本机ip地址192. ...
- P2220 [HAOI2012]容易题【快速幂】
题目描述 为了使得大家高兴,小Q特意出个自认为的简单题(easy)来满足大家,这道简单题是描述如下: 有一个数列A已知对于所有的A[i]都是1~n的自然数,并且知道对于一些A[i]不能取哪些值,我们定 ...
- (win7) 在IIS6.0 中配置项目
1.进入IIS管理器 右击“计算机”->管理->服务和应用程序->Internet信息服务(IIS)管理器 2.将项目加入IIS中 网站->右击“默认网站”->添加虚拟目 ...
- Python之浅谈模块
目录 模块的四种形式 什么是模块 import和from......import 循环导入 模块的搜索路径 Python文件的两种用途 random模块 模块的四种形式 什么是模块 模块就是一个p ...
- 比Minikube更快,使用Kind快速创建K8S学习环境
简述 K8S 如火如荼的发展着,越来越多人想学习和了解 K8S,但是由于 K8S 的入门曲线较高很多人望而却步. 然而随着 K8S 生态的蓬勃发展,社区也呈现了越来越多的部署方案,光针对生产可用的环境 ...
- 记一次解密wireshark抓取的冰蝎通信流量
一.关于冰蝎 1.1 简单介绍 冰蝎是一款基于Java开发的动态加密通信流量的新型Webshell客户端.老牌 Webshell 管理神器——中国菜刀的攻击流量特征明显,容易被各类安全设备检测,实际场 ...