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 --后缀树解法的更多相关文章

  1. 从Trie树(字典树)谈到后缀树

    转:http://blog.csdn.net/v_july_v/article/details/6897097 引言 常关注本blog的读者朋友想必看过此篇文章:从B树.B+树.B*树谈到R 树,这次 ...

  2. [算法]从Trie树(字典树)谈到后缀树

    我是好文章的搬运工,原文来自博客园,博主July_,地址:http://www.cnblogs.com/v-July-v/archive/2011/10/22/2316412.html 从Trie树( ...

  3. 笔试算法题(40):后缀数组 & 后缀树(Suffix Array & Suffix Tree)

    议题:后缀数组(Suffix Array) 分析: 后缀树和后缀数组都是处理字符串的有效工具,前者较为常见,但后者更容易编程实现,空间耗用更少:后缀数组可用于解决最长公共子串问题,多模式匹配问题,最长 ...

  4. BZOJ 3238: [Ahoi2013]差异((单调栈+后缀数组)/(后缀树))

    [传送门[(https://www.lydsy.com/JudgeOnline/problem.php?id=3238) 解题思路 首先原式可以把\(len\)那部分直接算出来,然后通过后缀数组求\( ...

  5. 后缀树(suffix tree)

    参考: 从前缀树谈到后缀树 后缀树 Suffix Tree-后缀树 字典树(trie树).后缀树 一.前缀树 简述:又名单词查找树,tries树,一种多路树形结构,常用来操作字符串(但不限于字符串), ...

  6. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  7. 后缀树系列一:概念以及实现原理( the Ukkonen algorithm)

    首先说明一下后缀树系列一共会有三篇文章,本文先介绍基本概念以及如何线性时间内构件后缀树,第二篇文章会详细介绍怎么实现后缀树(包含实现代码),第三篇会着重谈一谈后缀树的应用. 本文分为三个部分, 首先介 ...

  8. 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树

    另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...

  9. [转载]字典树(trie树)、后缀树

    (1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边 ...

随机推荐

  1. linux 系统文件目录颜色及特殊权限对应的颜色

    什么决定文件目录的颜色和背景?  颜色  说明  栗子  权限 白色 表示普通文件   蓝色 表示目录  绿色 表示可执行文件 浅蓝色 链接文件 黄色 表示设备文件 红色   表示压缩文件 红色闪烁 ...

  2. DataFrame索引和切片

    import numpy as np import pandas as pd from pandas import DataFrame, Seriesdf = DataFrame(data=np.ra ...

  3. JavaScript 格式化数字、金额、千分位、保留几位小数、舍入舍去…

    JavaScript 格式化数字.金额.千分位.保留几位小数.舍入舍去… 类库推荐 1. Numeral.js 一个用于格式化和操作数字的JavaScript库.数字可以被格式化为货币,百分比,时间, ...

  4. MFC 添加C++类,别的类不通过C++类的定义的对象就可以直接调用C++类里面的成员函数;

    MFC 添加C++类,不用定义C++类的对象,别的类不通过C++类的定义的对象就可以直接调用C++类里面的成员函数: 1先在mfc程序中添加普通类CProdata,然后删除头文件Prodata.h里面 ...

  5. 通过网络经过路由器映射电脑ip到外网路由器

    一.内网自己电脑做服务器 自己电脑ip为192.168.0.104 首先先把自己路由器ip192.168.1.82映射到外网路由器,其中外网路由器ip为59.40.0.196 让后本机ip地址192. ...

  6. P2220 [HAOI2012]容易题【快速幂】

    题目描述 为了使得大家高兴,小Q特意出个自认为的简单题(easy)来满足大家,这道简单题是描述如下: 有一个数列A已知对于所有的A[i]都是1~n的自然数,并且知道对于一些A[i]不能取哪些值,我们定 ...

  7. (win7) 在IIS6.0 中配置项目

    1.进入IIS管理器 右击“计算机”->管理->服务和应用程序->Internet信息服务(IIS)管理器 2.将项目加入IIS中 网站->右击“默认网站”->添加虚拟目 ...

  8. Python之浅谈模块

    目录 模块的四种形式 什么是模块 import和from......import 循环导入 模块的搜索路径 Python文件的两种用途 random模块 模块的四种形式 什么是模块 ​ 模块就是一个p ...

  9. 比Minikube更快,使用Kind快速创建K8S学习环境

    简述 K8S 如火如荼的发展着,越来越多人想学习和了解 K8S,但是由于 K8S 的入门曲线较高很多人望而却步. 然而随着 K8S 生态的蓬勃发展,社区也呈现了越来越多的部署方案,光针对生产可用的环境 ...

  10. 记一次解密wireshark抓取的冰蝎通信流量

    一.关于冰蝎 1.1 简单介绍 冰蝎是一款基于Java开发的动态加密通信流量的新型Webshell客户端.老牌 Webshell 管理神器——中国菜刀的攻击流量特征明显,容易被各类安全设备检测,实际场 ...