[USACO07DEC]Best Cow Line G

[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 || 后缀数组的更多相关文章

  1. 【BZOJ4556】字符串(后缀数组,主席树)

    [BZOJ4556]字符串(后缀数组,主席树) 题面 BZOJ 题解 注意看题: 要求的是\([a,b]\)的子串和[c,d]的\(lcp\)的最大值 先来一下暴力吧 求出\(SA\)之后 暴力枚举\ ...

  2. 【LOJ#3095】[SNOI2019]字符串(后缀数组)

    [LOJ#3095][SNOI2019]字符串(后缀数组) 题面 LOJ 题解 首先画图看看如何比较两个串的大小,发现这个东西等价于求两个相邻的后缀的\(LCP\). 一个做法是求出\(SA\),然后 ...

  3. G 唐纳德与子串(easy)(华师网络赛---字符串,后缀数组)(丧心病狂的用后缀自动机A了一发Easy)

    Time limit per test: 1.0 seconds Memory limit: 256 megabytes 子串的定义是在一个字符串中连续出现的一段字符.这里,我们使用 s[l…r] 来 ...

  4. 字符串(后缀数组||SAM):NOI2015 品酒大会

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAByIAAALuCAIAAABtq0bwAAAgAElEQVR4nOy9f2wb150vev4boESeln ...

  5. 2016vijos 1-1 兔子的字符串(后缀数组 + 二分 + 哈希)

    题意: 给出一个字符串,至多将其划分为n部分,每一部分取出字典序最大的子串ci,最小化 最大的ci 先看一个简化版的问题: 给一个串s,再给一个s的子串t,问能否通过将串划分为k个部分,使t成为划分后 ...

  6. BZOJ 4556: [Tjoi2016&Heoi2016]字符串(后缀数组 + 二分答案 + 主席树 + ST表 or 后缀数组 + 暴力)

    题意 一个长为 \(n\) 的字符串 \(s\),和 \(m\) 个询问.每次询问有 \(4\) 个参数分别为 \(a,b,c,d\). 要你告诉它 \(s[a...b]\) 中的所有子串 和 \(s ...

  7. [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...

  8. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  9. 【字符串】后缀数组SA

    后缀数组 概念 实际上就是将一个字符串的所有后缀按照字典序排序 得到了两个数组 \(sa[i]\) 和 \(rk[i]\),其中 \(sa[i]\) 表示排名为 i 的后缀,\(rk[i]\) 表示后 ...

随机推荐

  1. Hugo博客搭建

    HUGO + Github + Github Action持续集成部署个人博客 HUGO本地环境 首先在HUGO的官网下载Hugo的Windows安装包,然后将路径添加到环境变量即可. step1:下 ...

  2. SpringMVC转发及重定向

    基础环境搭建请参考SringMVC入门程序 1:springmvc-servlet.xml <?xml version="1.0" encoding="UTF-8& ...

  3. 《工程热力学沈维道童钧耕第四版-带书签》高清pdf下载链接

    <工程热力学沈维道童钧耕第四版-带书签>高清pdf下载链接 百度网盘链接:https://pan.baidu.com/s/1dWksA8O3y2JSfIQy5lrU5g 提取码:7x9w ...

  4. Python修改paramiko模块开发运维审计保垒机

    目前市面上,专门做IT审计堡垒机的厂商有很多,他们的产品都有一个特点,那就是基本上每台的售价都在20万以上.像我们做技术的,不可能每次待的公司都是大公司,那么在小公司,是不太可能投资20多万买一台硬件 ...

  5. 立体匹配-----NCC视差匹配

    目录 一.立体匹配算法 1.立体匹配算法分类 二.NCC 视差匹配方法 1.原理 2.NCC计算公式 3.算法流程 4.代码实现     5.不同场景运行 三.结论 四.遇到的问题及解决方法 一.立体 ...

  6. java中String StringBuilder StringBuffer比较和效率(性能)测试

    string stringbuilder stringbuffer三者的区别 从JDK源码看,String.StringBuilder.StringBuffer都是存放在char[] 数组字符串. 简 ...

  7. JDBC 进阶:使用封装通用DML DQL 和结构分层以及at com.mysql.jdbc.PreparedStatement.setTimestamp空指针异常解决

    准备: 数据表 CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(10) DEFAULT ...

  8. 一图解析MongoDB

    了解MongoDB,这一张图就够了: 版权所有,转载请注明出处.

  9. 解决Macbook Pro 2017安装Windows10双系统后在Windows系统中Apple蓝牙鼠标不能使用问题

    MAC BOOK PRO 2017安装Windows10双系统 在Windows系统中蓝牙鼠标不能使用解决办法 最近因工作需要,macOS系统不能要求,大部分工作必须要在Windows系统中进行,最初 ...

  10. LeetCode 面试题56 - I. 数组中数字出现的次数 | Python

    面试题56 - I. 数组中数字出现的次数 题目 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). ...