[USACO07DEC]Best Cow Line G 字符串hash || 后缀数组
[USACO07DEC]Best Cow Line G
小声哔哔:字符串hash牛逼
题意
给出一个字符串,每次可以从字符串的首尾取出一个字符,放到队列的尾部,求可以得到的最小的字典序是多少?
思路1
此时字符串首尾的下标分别为l,r。
如果str[l]!=str[r]:取较小的字符串
如果str[l]==str[r]:找到第一个非负整数x,使得str[l+x]!=str[r-x]。
如果str[l+x]<str[r-x],那么此时取str[l],否则取str[r];
数据范围是\(1 \leq N \leq 5\times10^5\),如果暴力复杂度比较高
如果快速找到x是关键,我想了字符串hash,没想到二分判断条件。(我好菜啊啊啊啊啊啊啊)
对于l,r,我们二分x的值,找到第一个hash值不想等的x。
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e6+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
char str[N];
string ans;
ull bin[N],hash1[N],hash2[N];
int n;
void init()
{
bin[0]=1;
for(int i=1; i<=n; i++)
{
bin[i]=bin[i-1]*137;
hash1[i]=hash1[i-1]*137+str[i]-'a'+1;
hash2[i]=hash2[i-1]*137+str[n-i+1]-'a'+1;
}
}
ull get1(int l,int r)
{
return hash1[r]-hash1[l-1]*bin[r-l+1];
}
ull get2(int l,int r)
{
return hash2[r]-hash2[l-1]*bin[r-l+1];
}
int solve(int aga,int en)
{
int l=0,r=(en-aga)/2,ans=0;
while(l<=r)
{
int mid=(l+r)/2;
if(get1(aga,aga+mid)!=get2(n+1-en,n+1-en+mid))
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
getchar();
scanf("%c",&str[i]);
}
init();
int l=1,r=n;
while(l<=r)
{
if(l==r)
{
ans+=str[l];
break;
}
if(str[l]<str[r])
ans+=str[l++];
else if(str[l]>str[r])
ans+=str[r--];
else
{
int len=solve(l,r);
if(str[l+len]<str[r-len])
ans+=str[l++];
else
ans+=str[r--];
}
}
for(int i=0; i<ans.size(); i++)
{
printf("%c",ans[i]);
if((i+1)%80==0)
printf("\n");
}
return 0;
}
思路2
pre[i]表示以i开头的前缀(即把以i结尾的前缀倒过来)
suf[i]表示以i开头的后缀
当str[l]==str[r]的时候,我们只需要比较pre[r]和suf[l]的排名
对于pre[r],我们在结尾加一个字符,然后把原串反过来添加到末尾,求后缀数组,pre[r]就是suf[n+n-r+2]。
比如acabca,处理完就是acabca#acbaca
比较两个c的时候,其实就是比较第一个c的排名和倒数第二个c的排名
代码
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
char str[N];
string ans;
int n, m, sa[N], rk[N], oldrk[N<<1], pos[N], cnt[N];
bool cmp(int x, int y, int k)
{
return oldrk[x] == oldrk[y] && oldrk[x + k] == oldrk[y + k];
}
void getsa()
{
m = 122;
for (int i = 1; i <= n; i++)
++cnt[rk[i] = str[i]];
for (int i = 1; i <= m; i++)
cnt[i] += cnt[i - 1];
for (int i = n; i; i--)
sa[cnt[rk[i]]--] = i;
for (int k = 1; k <= n; k <<= 1)
{
int num = 0;
for (int i = n - k + 1; i <= n; i++)
pos[++num] = i;
for (int i = 1; i <= n; i++)
{
if (sa[i] > k)
pos[++num] = sa[i] - k;
}
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
++cnt[rk[i]];
for(int i=1;i<=m;i++)
cnt[i]+=cnt[i-1];
for (int i = n; i; i--)
sa[cnt[rk[pos[i]]]--] = pos[i];
num = 0;
memcpy(oldrk, rk, sizeof(rk));
for (int i = 1; i <= n; i++)
rk[sa[i]]=cmp(sa[i],sa[i-1],k)?num:++num;
if(num==n) break;
m=num;
}
for(int i=1;i<=n;i++)
rk[sa[i]]=i;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
getchar();
scanf("%c",&str[i]);
}
str[n+1]='0';
for(int i=n+2;i<=n*2+1;i++)
str[i]=str[2*n-i+2];
n=n*2+1;
getsa();
int l=1,r=n/2;
while(l<=r)
{
if(str[l]<str[r]) ans+=str[l++];
else if(str[l]>str[r]) ans+=str[r--];
else
{
if(rk[l]<rk[n/2+n/2-r+2]) ans+=str[l++];
else ans+=str[r--];
}
}
for(int i=0;i<ans.size();i++)
{
printf("%c",ans[i]);
if((i+1)%80==0) printf("\n");
}
return 0;
}
/*
6
a
c
a
b
c
b
*/
[USACO07DEC]Best Cow Line G 字符串hash || 后缀数组的更多相关文章
- 【BZOJ4556】字符串(后缀数组,主席树)
[BZOJ4556]字符串(后缀数组,主席树) 题面 BZOJ 题解 注意看题: 要求的是\([a,b]\)的子串和[c,d]的\(lcp\)的最大值 先来一下暴力吧 求出\(SA\)之后 暴力枚举\ ...
- 【LOJ#3095】[SNOI2019]字符串(后缀数组)
[LOJ#3095][SNOI2019]字符串(后缀数组) 题面 LOJ 题解 首先画图看看如何比较两个串的大小,发现这个东西等价于求两个相邻的后缀的\(LCP\). 一个做法是求出\(SA\),然后 ...
- G 唐纳德与子串(easy)(华师网络赛---字符串,后缀数组)(丧心病狂的用后缀自动机A了一发Easy)
Time limit per test: 1.0 seconds Memory limit: 256 megabytes 子串的定义是在一个字符串中连续出现的一段字符.这里,我们使用 s[l…r] 来 ...
- 字符串(后缀数组||SAM):NOI2015 品酒大会
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAByIAAALuCAIAAABtq0bwAAAgAElEQVR4nOy9f2wb150vev4boESeln ...
- 2016vijos 1-1 兔子的字符串(后缀数组 + 二分 + 哈希)
题意: 给出一个字符串,至多将其划分为n部分,每一部分取出字典序最大的子串ci,最小化 最大的ci 先看一个简化版的问题: 给一个串s,再给一个s的子串t,问能否通过将串划分为k个部分,使t成为划分后 ...
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串(后缀数组 + 二分答案 + 主席树 + ST表 or 后缀数组 + 暴力)
题意 一个长为 \(n\) 的字符串 \(s\),和 \(m\) 个询问.每次询问有 \(4\) 个参数分别为 \(a,b,c,d\). 要你告诉它 \(s[a...b]\) 中的所有子串 和 \(s ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
- BZOJ 2865 字符串识别 | 后缀数组 线段树
集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...
- 【字符串】后缀数组SA
后缀数组 概念 实际上就是将一个字符串的所有后缀按照字典序排序 得到了两个数组 \(sa[i]\) 和 \(rk[i]\),其中 \(sa[i]\) 表示排名为 i 的后缀,\(rk[i]\) 表示后 ...
随机推荐
- Go gRPC进阶-go-grpc-middleware使用(八)
前言 上篇介绍了gRPC中TLS认证和自定义方法认证,最后还简单介绍了gRPC拦截器的使用.gRPC自身只能设置一个拦截器,所有逻辑都写一起会比较乱.本篇简单介绍go-grpc-middleware的 ...
- [转载]绕过CDN查找真实IP方法总结
前言 类似备忘录形式记录一下,这里结合了几篇绕过CDN寻找真实IP的文章,总结一下绕过CDN查找真实的IP的方法 介绍 CDN的全称是Content Delivery Network,即内容分发网络. ...
- [PHP] 生成二维码(两种方法)
方法一:(调用google二维码接口,本人测试网不好,不好用!) <?php //1.封装生成二维码图片的函数(方法) /** *利用google api生成二维码图片 * $content:二 ...
- 最新VMware虚拟机安装Linux系统-CentOS(详细教程)
一.前言 最近有网友反应初学Linx不会安装,找了许多教程不是太全面,总会遇到一些要不是启动不了,要不是连不上网,各种问题,为了让大家能够顺利的安装,小乐写了一个非常详细的教程,让大家少入坑. 二.背 ...
- 转载-linux内核长什么样
来源:Linux中国 今天,我来为大家解读一幅来自 TurnOff.us 的漫画 "InSide The Linux Kernel" . TurnOff.us是一个极客漫画网站,作 ...
- 聊一聊JSONP和图像Ping的区别
JSONP 在讲 JSONP 之前需要再来回顾一下在页面上使用 script 引入外部的 js 文件时到底引入了什么? 先建立一个 index.js 文件. console.log(123) 再建立一 ...
- sqli lab less-5-6
less-5 基于报错的注入 基于报错可以爆出当前数据库名等等 id=2' and extractvalue(1, concat(0x7c,(select user())));-- # ?id=2' ...
- Python获取当前 年 月 日
import datetime datetime.datetime.now().year datetime.datetime.now().month datetime.datetime.now().d ...
- C# 9 新特性:代码生成器、编译时反射
前言 今天 .NET 官方博客宣布 C# 9 Source Generators 第一个预览版发布,这是一个用户已经喊了快 5 年特性,今天终于发布了. 简介 Source Generators 顾名 ...
- JavaScript面向对象的作用域链(转载)
JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕... 一.“JavaScript中无块级作用域” 在Java或C# ...