P4094 字符串

news/2025/1/15 21:32:28/文章来源:https://www.cnblogs.com/hangry/p/18402070

P4094 字符串

简化题意

给定字符串 \(s\), 每次询问给定两个字符串 \([a , b]\)\([c , d]\), 求前串的所有子串和后串的最长公共前缀。
\(n \le 10 ^ 5 , m \le 10 ^ 5\)

题解

感觉其实这道题并不是特别难的,就是代码长,不折不扣的码农题。

刚开始有一个错误的想法,就是主席树维护 \(Rank\) 值查离 \(Rank_c\) 前驱后继两个值。

这个显然就是错的,因为可能匹配出的 \(LCP\) 值越过了 \(b\),导致可能前面的长度要更优。

正解是二分答案,当钦定答案为 \(mid\) 时,显然区间为 \([a , b - mid + 1]\)

先二分 \(Rank_y\) 上下界,使上下界为第一个和最后一个与 \(y \ LCP \ge mid\) 的位置。

主席树上查在这个区间是否有值即可。

复杂度为 \(\mathcal{O}(n \log^2 n)\)

不卡常。

Code

CODE
#include <bits/stdc++.h>
using namespace std ; 
const int N = 1e5 + 100 ; 
char s[N] ; 
int n , M ; namespace CharacterString {namespace SuffixArray {/*This is a algorithm in order to find suffixes and sort for them with the time complexity O(nlogn).It supports the operation of getting the longest prefix.*/int SA[N] , Rank[N] , m = 127 , p , Lstrk[N] , id[N] , cnt[N] , key1[N] ; int lg[N] , ST[21][N] ; inline void Suffix_Array() {memset(cnt , 0 , sizeof(cnt)) ; auto Compare = [](int x , int y , int j) {return Lstrk[x] == Lstrk[y] && Lstrk[x + j] == Lstrk[y + j] ; } ; for (int i = 1 ; i <= n ; ++ i) cnt[Rank[i] = s[i]] ++ ; for (int i = 1 ; i <= m ; ++ i) cnt[i] += cnt[i - 1] ; for (int i = n ; i >= 1 ; -- i) SA[cnt[Rank[i]] --] = i ; for (int j = 1 ; ; m = p , j <<= 1) {p = 0 ; for (int i = n ; i > n - j ; -- i) id[++ p] = i ; for (int i = 1 ; i <= n ; ++ i) {if (SA[i] - j > 0) {id[++ p] = SA[i] - j ; }}memset(cnt , 0 , sizeof(cnt)) ; for (int i = 1 ; i <= n ; ++ i) {++ cnt[key1[i] = Rank[id[i]]] ; }for (int i = 1 ; i <= m ; ++ i) cnt[i] += cnt[i - 1] ; for (int i = n ; i >= 1 ; -- i) {SA[cnt[key1[i]] --] = id[i] ; }memcpy(Lstrk , Rank , sizeof(Rank)) ; p = 0 ; for (int i = 1 ; i <= n ; ++ i) {Rank[SA[i]] = Compare(SA[i] , SA[i - 1] , j) ? p : ++ p ; }if (p == n) break ; } }inline int MinST(int l , int r) {int k = lg[r - l + 1] ; return min(ST[k][l] , ST[k][r - (1 << k) + 1]) ; }inline int LCP(int l , int r) {if (l == r) return n - l + 1 ; if (Rank[l] > Rank[r]) swap(l , r) ; return MinST(Rank[l] + 1 , Rank[r]) ; }inline void GetHeight() {for (int i = 2 ; i <= n ; ++ i) lg[i] = lg[i >> 1] + 1 ; for (int i = 1 , k = 0 ; i <= n ; ++ i) {if (!Rank[i]) continue ; if (k) k -- ; while (s[i + k] == s[SA[Rank[i] - 1] + k]) ++ k ; ST[0][Rank[i]] = k ; }for (int j = 1 ; j <= lg[n] ; ++ j) {for (int i = 1 ; i <= n - (1 << j) + 1 ; ++ i) {ST[j][i] = min(ST[j - 1][i] , ST[j - 1][i + (1 << (j - 1))]) ; }}}}
} using namespace CharacterString ; 
using namespace SuffixArray ; namespace Chairman_Tree {#define lson(x) t[x].son[0]#define rson(x) t[x].son[1]#define data(x) t[x].data#define mid ((l + r) >> 1)struct Node {int son[2] , data ; } t[N * 200] ; int root[N] , numbol ; void updata(int &now , int id , int l , int r , int x) {t[now = ++ numbol] = t[id] ; if (l == r) return t[now].data ++ , void() ; if (x <= mid) updata(lson(now) , lson(id) , l , mid , x) ; else updata(rson(now) , rson(id) , mid + 1 , r , x) ; t[now].data = t[lson(now)].data + t[rson(now)].data ; }bool check(int fir , int sec , int l , int r , int x , int y) {if (x <= l && r <= y) return (data(fir) - data(sec) != 0) ; bool flag = 0 ; if (x <= mid) flag |= check(lson(fir) , lson(sec) , l , mid , x , y) ; if (y > mid) flag |= check(rson(fir) , rson(sec) , mid + 1 , r , x , y) ; return flag ; }#undef mid
} using namespace Chairman_Tree ; pair <int , int> Find(int now , int x) {int left = 1 , right = now - 1 , ans1 = now , ans2 = now ; while (left <= right) {int mid = (left + right) >> 1 ; if (LCP(SA[mid] , SA[now]) >= x) {ans1 = mid ; right = mid - 1 ; } else left = mid + 1 ; }left = now + 1 , right = n ; while (left <= right) {int mid = (left + right) >> 1 ; if (LCP(SA[mid] , SA[now]) >= x) {left = mid + 1 ; ans2 = mid ; } else right = mid - 1 ; }return make_pair(ans1 , ans2) ; 
}signed main() {// freopen("1.in" , "r" , stdin) ; // freopen("1.out" , "w" , stdout) ; ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ; cin >> n >> M ; for (int i = 1 ; i <= n ; ++ i) cin >> s[i] ; Suffix_Array() ; GetHeight() ; int ans = 0 ; pair <int , int> mp ; for (int i = 1 ; i <= n ; ++ i) updata(root[i] , root[i - 1] , 1 , n , Rank[i]) ; for (int i = 1 , w , x , y , z ; i <= M ; ++ i) {cin >> w >> x >> y >> z ; ans = 0 ; int left = 1 , right = min(x - w + 1 , z - y + 1) ; while (left <= right) {int mid = (left + right) >> 1 ; mp = Find(Rank[y] , mid) ; int L = mp.first , R = mp.second ; if (check(root[x - mid + 1] , root[w - 1] , 1 , n , L , R)) {left = mid + 1 ; ans = mid ; } else right = mid - 1 ; }cout << ans << '\n' ; }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/793723.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java】爬取澳门区划信息

官网地址:https://macaostreets.iam.gov.mo/zh_mo/freguesiaindex.html大区部分是在页面展示的 点击发现并没有请求网络,所以数据是js中存在的 找到了展示街道方法,这一段: 使用大区id匹配上述变量的function showStreets(freguesia){var freguesiaStreets;switch(fregue…

『模拟赛』CSP-S模拟2

『模拟赛记录』CSP-S模拟2Rank 非常好数据,使我成为 Rank1(雾数据换源后的狂流——齐秦北风在吹着清冷的街道 街灯在拉开长长的影子 走过的路 想过的事 仿佛越来越远越来越长 越来越多越难以抛开 多少平淡日子以来的夜晚 你曾是我渴望拥有的企盼 太多分手的记忆 仿佛越来越远…

(更新至 8/25) 不是暑假的暑假的不是游记的游记

持续更新中 Day1 - 8/23 因为在学校里待不下去了,所以订的十一点多的火车,打算八点钟就出门 结果教练在家长群里发我们十二点放假,所以我爸怕我赶不上就帮忙改签到一点半了 你说的对,但是为什么改成卧铺了??? 因此因为xfg的莫名其妙原因,还是决定十点钟出来 那么十点钟…

在 Alt + Tab 列表中隐藏指定窗口

安装并启动 AlexanderPro/SmartContextMenu,然后在指定窗口上Ctrl + rightClick,在出现的菜单中勾选在 Alt + Tab 列表中隐藏即可。这个程序还提供了置顶、调整透明度等功能,挺实用。

4-网络安全体系与网络安全模型

4.1 网络安全体系概述 1)概念 一般而言,网络安全体系是网络安全保障系统的最高层概念抽象,是由各种网络安全单元按照一定的规则组成的,共同实现网络安全的目标。 网络安全体系包括法律法规政策文件、安全策略、组织管理、技术措施、标准规范、安全建设与运营、人员队伍、教…

2024软件工程课程第一次个人作业

这个作业属于哪个课程 福州大学-软件工程2024这个作业要求在哪里 202409软件工程课程第一次个人作业这个作业的目标 初步使用博客园和GitHub,增强在博客园学习的意识和提升软件开发实践技能的意识,让老师和助教了解各个同学的水平学号 0723052261. 个人logo文生图任务个人风格…

【Hashcat工具】工具使用

数字破解 a、7位数字破解 hashcat64.exe -a 3 -m 0 --force 25c3e88f81b4853f2a8faacad4c871b6 ?d?d?d?d?d?d?db、7位小写字母破解 hashcat64.exe -a 3 -m 0 --force 7a47c6db227df60a6d67245d7d8063f3 ?l?l?l?l?l?l?lc、1-8位数字破解 hashcat64.exe -a 3 -m 0 …

洛谷 P4829 kry loves 2048——题解

洛谷P4829题解传送锚点摸鱼环节 kry loves 2048 题目背景 kls是一个人赢。 题目描述 kls最近在玩一款类似2048的游戏,规则是这样的: 一开始,有\(n\)个方块,每个方块上有一个\(1\)到\(m\)的整数。 kls可以进行两种操作:选择两个数字相同的方块(不一定要相邻),将它们合并…

代码整洁之道--读书笔记(4)

代码整洁之道简介: 本书是编程大师“Bob 大叔”40余年编程生涯的心得体会的总结,讲解要成为真正专业的程序员需要具备什么样的态度,需要遵循什么样的原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来者引路,助其职业生涯迈上更…

Javaweb-DQL-分页查询

1. select * from stu limit 0,3; 2. select * from stu limit 0,3; 3. select * from stu limit 3,3; 4. select * from stu limit 6,3;

有哪些让你「 爽到爆炸 」的 Windows 软件?

前言 本文源于知乎的一个提问,如标题所示:有哪些让你「 爽到爆炸 」的 Windows 软件?今天大姚给大家分享6款C#/.NET开源且免费的Windows软件,希望可以帮助大家提高学习、开发、办公效率。 Microsoft PowerToys项目简介: Microsoft PowerToys 是使用 C++ 和 C# 编程语言开发…

LeetCode刷题 堆

不会做简单题目的小菜菜!一:堆 1、一种二叉树的结构(完全二叉树) 2、完全二叉树:从上到下;从左到右;填满 3、最大堆:根节点的权值大于孩子节点 4、最小堆:根节点的权值依次小于孩子节点 5、常用操作 #创建堆(最大堆,最小堆) #添加元素 #获取堆顶元素 #删除堆顶元素…