[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. 基于 HTML5 WebGL 的高炉炼铁厂可视化系统

    前言       在当今 工业4.0 新时代的推动下,不仅迎来了 工业互联网 的发展,还开启了 5G 时代的新次元.而伴随着带宽的提升,网络信息飞速发展,能源管控上与实时预警在工业互联网中也占着举足轻 ...

  2. kioptrix靶机记录

    靶机地址:172.16.1.193 Kali地址:172.16.1.107 首页为Apache测试页,没看到有价值信息 尝试目录扫描: 点击查看: http://172.16.1.193/index. ...

  3. Acid靶机渗透

    Acid渗透靶机实战 攻击机:kali 192.168.41.147 靶机: acid 192.168.41.149 信息收集 ip发现 开启Acid靶机,通过nmap进行局域网存火主机扫描.![]( ...

  4. HTML+CSS教程(六)浮动-float+定位-position+居中问题

    一.浮动(float)1.文档流:是指盒子按照 html 标签编写的顺序依次从上到下,从左到右排列,块元素占一行,行内元素在一行之内从左到右排列,先写的先排列,后写的排在后面,每个盒子都占据自己的位置 ...

  5. 基于 Python 的自动文本提取:抽象法和生成法的比较

    我们将现有的 提取方法(Extractive)(如LexRank,LSA,Luhn和Gensim现有的TextRank摘要模块)与含有51个文章摘要对的Opinosis数据集进行比较.我们还尝试使用T ...

  6. mybatis源码学习:插件定义+执行流程责任链

    目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...

  7. 使用pthread进行编程

    使用pthread进行并行编程 进程与线程 进程是一个运行程序的实例:线程像一个轻量级的进程:在一个共享内存系统中,一个进程可以有多个线程 POSIX® Threads: 即 Pthreads,是一个 ...

  8. 文件读取的时候要留意 -- 序列化serialize()与反序列化unserialize():

    原文:https://blog.csdn.net/little_rabbit_baby/article/details/53840543 序列化serialize()与反序列化unserialize( ...

  9. 编译原理-第四章 语法分析-4.6 简单LR技术

    简单LR分析方法 一.LR语言分析器模型与算法 1.输入.输出.栈和方法 2.LR语法分析表 3.LR分析程序 4.例 例1: 例2: 二.LR语法分析算法 1.LR语法分析算法的定义和概念 定义: ...

  10. List of common SCSI KCQs

    Category Key ASC ASCQ Error Condition No Sense 0 00 00 No error 0 5D 00 No sense - PFA threshold rea ...