[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. 再看CVE-2018-12613 phpmyadmin后台文件包含&&RPO攻击

    写在前面 因为看了朋友的一篇分析又回头想了想自己去年遇到的这个纠结的问题. 去年写过一篇phpmyadmin后台文件包含的文章,写的非常的草草,并没有分析的过程,只是把自己的问题记了下来.当时纠结于最 ...

  2. Redis 5.0.9 安装

    目录 系统环境 系统版本 内核版本 安装步骤 安装 gcc 依赖 下载 Redis 解压 Redis 切换到 redis 解压目录下,执行编译 指定目录安装 启动 Redis 服务 最后 系统环境 系 ...

  3. MySQL 主从复制:基于二进制文件复制配置详解

    MySQL-主从复制:基于二进制文件复制详解 前言 主从复制是指把一个MySQL的数据库服务器作为主服务器(master),然后把master的数据复制到一个或者多个MySQL数据库服务器作为从服务器 ...

  4. Kubernetes笔记(一):十分钟部署一套K8s环境

    Kubernetes是Goole开源的一个容器编排引擎,它支持自动化部署.大规模可伸缩.应用容器化管理 -- 百度百科. 接触K8s也有半年多了,也基于阿里云平台搭建了包含多级服务.目前运行较为稳定的 ...

  5. [Python进阶].pyc的那点事

    1. 什么是 .pyc文件 .pyc文件 就是 Python的字节码(byte-compiled)文件..py文件运行时,python会自动将其编译成PyCodeObject并写入.pyc文件,再有p ...

  6. css3 文本控制自动换行

    text-overflow:ellipsis; white-space:nowrap; overflow:hidden;

  7. Cannot find or open the PDB file

    打开VS:工具 --> 选项 --> 调试-->符号接下来就是选择Microsoft,然后确认

  8. hdu3033 I love sneakers! 分组背包变形(详解)

    这个题很怪,一开始没仔细读题,写了个简单的分组背包交上去,果不其然WA. 题目分析: 分组背包问题是这样描述的:有K组物品,每组 i 个,费用分别为Ci ,价值为Vi,每组物品是互斥的,只能取一个或者 ...

  9. [软件共享]将数据库中的数据导出为SQL脚本

    可以直接将数据库中的数据导出为脚本,并可以自己设置过滤条件.使用方法很简单,不在多说了.下面是软件截图.123 下载:http://files.cnblogs.com/pw/mssql2.rar

  10. python自动化测试开发利器ulipad最佳实践(可写python测试代码也可编写selenium、Appium等)...

    介绍 UliPad是一个国人开发的python轻量级编辑器,导向和灵活的编程器.它如类浏览器,代码自动完成许多功能,如:HTML查看器,目录浏览器,向导等. 下载与安装 下载地址:https://py ...