[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. CTF中的命令执行绕过

    本位原创作者:Smity 在介绍命令注入之前,有一点需要注意:命令注入与远程代码执行不同.他们的区别在于,远程代码执行实际上是调用服务器网站代码进行执行,而命令注入则是调用操作系统命令进行执行. 作为 ...

  2. Q - QQpet exploratory park HDU - 1493 (概率DP)

    题目大意: 一共有61个位置,标记为0~60.其中有10个重要位置,分别为:5, 12, 22, 29, 33, 38, 42, 46, 50 and 55. 有一个筛子,一共6个面,标有1~6.摇到 ...

  3. 掷骰子 dp

    B. 掷骰子 单点时限: 2.0 sec 内存限制: 512 MB 骰子,中国传统民间娱乐用来投掷的博具,早在战国时期就已经被发明. 现在给你 n 个骰子,求 n 个骰子掷出点数之和为 a 的概率是多 ...

  4. Volatile的应用DCL单例模式(四)

    Volatile的应用 单例模式DCL代码 首先回顾一下,单线程下的单例模式代码 /** * 单例模式 * * @author xiaocheng * @date 2020/4/22 9:19 */ ...

  5. SpringBoot系列(九)单,多文件上传的正确姿势

    SpringBoot系列(九)分分钟解决文件上传 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配 ...

  6. 功能测试--聊天功能测试&微信聊天

    微信聊天功能测试 发送对象 普通用户.公众号.群.其他特殊主体 衍生功能 转发.语音转文字.删除等 消息发送 单聊.群聊.语音.文字.图片.表情.链接.字符及长度 消息管理 发布通知.接受通知.发文件 ...

  7. [YII2] 视图层过滤客户恶意代码

    两种方式: 一种是吧html的恶意标签转译:(注意的就是命名空间) <?php use yii\helpers\Html; ?> <h1><?=Html::encode( ...

  8. 【题解】P2480 [SDOI2010]古代猪文 - 卢卡斯定理 - 中国剩余定理

    P2480 [SDOI2010]古代猪文 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 猪王国的文明源远流长,博大精 ...

  9. HTML+CSS教程(五)外联样式、组选择器、圆角边框、样式优先级、伪类、盒子模型、元素溢出

    一.外联样式 通过link标签引入外部css文件夹中的xxx.css文件到head标签中 例: 二. 1.组选择器 选择器名称1,选择器名称2,选择器名称3,…{属性:属性值;属性;属性值} 例: & ...

  10. 线程Event

    版本一: from threading import Event,current_thread,Thread import time event=Event() #造一个对象,内部维护一个全局变量,状 ...