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. Perl入门(三)Perl的数组

    Perl数组的声明方式 Perl使用"@"符号声明一个数组:@array: 使用"()"或"qw()"声明数组中元素: 一个完整的声明方式为 ...

  2. netty解决TCP的拆包和粘包的解决办法

    TCP粘包.拆包问题 熟悉tcp编程的可能知道,无论是服务端还是客户端,当我们读取或者发送数据的时候,都需要考虑TCP底层的粘包个拆包机制. tcp是一个“流”协议,所谓流就是没有界限的传输数据,在业 ...

  3. Snmp扫描-snmpwalk、snmpcheck

    SNMp经常被错误配置,是信息的金矿. SNMP服务是使用明文传输的,即使不能通过community进行查询,也有可能使用抓包嗅探的方法得到SNMP数据包中的数据. snmpwalk命令可以查询到很多 ...

  4. maven在idea中的配置的注意点

    1.基本的配置查看尚硅谷的文档链接在下 链接:https://pan.baidu.com/s/18gwll6gU38qNH2P01To-lQ 提取码:oq40 2.注意点: 需要将新建项目的配置也修改 ...

  5. Scrapy框架简介及小项目应用

    今天来总结一下Scrapy框架的用法.scrapy的架构如下: Engine  :引擎,处理整个系统的数据流处理.触发事务,是整个框架的核心. Items :项目,它定义了爬取结果的数据结构,爬取的数 ...

  6. 不就是语法和长难句吗—笔记总结Day4

    第六课 英语的特殊结构 1.强调句型 It is...that / who / which(少见)... * 强调句型可以强调句子中所有成分(唯一不能强调谓语)*  It is obviously t ...

  7. 程序员的修炼-我们为什么会编写BUG

    在最近的一周,我维护的业务系统出现了很多坏毛病,一周七天crash掉了4次,每次都需要都是因为一点很小的问题,触发了蝴蝶效应,导致整个系统全盘崩溃,于是产生除了叙述本篇的想法,当然这并不是为了掩盖我在 ...

  8. C++ MFC 操作文件夹及属性(新建,删除[包含子文件[夹]],剪切,复制,重命名)

    源文件:http://pan.baidu.com/s/169HCL 运行mfc缺失的动态连接库:http://pan.baidu.com/s/17pGlT 截图: 不足之处仅供参考,哈哈.

  9. C#模拟HTTP协议请求

    1       前言 http协议详解参考HTTP协议. WEB常用Get和Post请求. 地址栏参数:url为www.viwofer.com?id=12&name=viwofer则id=12 ...

  10. JavaScript图形实例:Hilbert曲线

    德国数学家David Hilbert在1891年构造了一种曲线,首先把一个正方形等分成四个小正方形,依次从西北角的正方形中心出发往南到西南正方形中心,再往东到东南角的正方形中心,再往北到东北角正方形中 ...