HDU 5442 后缀自动机+kmp
题目大意:
给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针
比赛一直因为处理最小位置出错,一结束就想明白了。。。真是作孽
这里正向后缀自动机跑一遍最大的,这样得到的位置肯定是最小的
而逆时针最大就反向重建后缀自动机再跑一遍最大的,但这样因为后缀自动机跑出的位置是最小的,但返回来就变成最大的位置了
如果存在更小的位置,那么就相当于可以从开头拎出一段移到末尾,那么必然是产生一个循环节,这个长度可以利用kmp来处理
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define M 26
#define N 80100
#define ull unsigned long long
char str[N] , tmp[N];
int n , num[N];
queue<int> Q; struct SamNode{
SamNode *son[M] , *f;
int l , s;
void init(){
for(int i= ; i<M ; i++) son[i] = NULL;
f = NULL;
l = s = ;
}
}; SamNode sam[N] , *root , *last;
int cnt; void init(){
cnt = ;
memset(sam , , sizeof(sam));
root = last = &sam[];
} void add(int x){
sam[cnt+].init();
SamNode *p = &sam[++cnt] , *jp=last;
/*
这里p->s = jp->s+1写成p->s = p->l也是一样的,因为对于每次当前的last来说,
l和s的值是一样的,因为每次当前的last都是处于字符串的位置上的
数,不是额外添加的节点
*/
p->l = jp->l+; p->s = jp->s+;
last = p;
for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p;
if(!jp) p->f=root;
else{
if(jp->l+ == jp->son[x]->l) p->f = jp->son[x];
else{
sam[cnt+].init();
SamNode *r = &sam[++cnt] , *q = jp->son[x];
*r=*q;
r->l = jp->l+ ; r->s = p->l;
q->f = p->f = r;
for(; jp && jp->son[x]==q ; jp=jp->f) jp->son[x]=r;
}
}
}
ull _hash = ; int _next[N];
int s[N];
void get_next(int *s , int n)
{
_next[] = _next[] = ;
for(int i=; i <n ; i++){
int j=_next[i-];
while(j && s[j+]!=s[i]) j =_next[j];
_next[i] = s[j+]==s[i]?j+:;
}
} int solve(int len)
{
SamNode *cur = root;
for(int i= ; i<len ; i++){
for(int j= ; j>= ; j--){
if(cur->son[j]){
s[i] = j;
cur = cur->son[j];
break;
}
}
}
int ret = cur->s-len+; return ret;
} int main() {
// freopen("a.in" , "r" , stdin);
// freopen("out.txt" , "w" , stdout);
int T;
scanf("%d" , &T);
while(T--){
scanf("%d" , &n);
scanf("%s" , str);
int len = n;
init();
for(int i= ; i<len ; i++)
str[len+i] = str[i];
for(int i= ; i<len* ; i++)
add(str[i]-'a');
// for(int i=0 ; i<2*n ; i++) cout<<str[i];
//cout<<endl;
int ret1 = solve(len); for(int i= ; i<len ; i++){
tmp[len-i-] = str[i];
}
for(int i= ; i<len ; i++){
tmp[len+i] = tmp[i];
} init();
for(int i= ; i<len* ; i++)
add(tmp[i]-'a');
int ret2 = solve(len);
ret2 = len-ret2+;
// cout<<"here: "<<ret1<<" "<<ret2<<endl;
get_next(s , n);
int del = n-_next[n-]-;
// cout<<"try: "<<del<<" "<<_next[n-1]<<endl;
if(n%del==){
while(ret2>del) ret2-=del;
}
// cout<<"here: "<<ret1<<" "<<ret2<<endl;
// cout<<ret1<<" "<<ret2<<endl;
int i , j , cnt , pos , wise=-;
for(i=ret1- , j=ret2+len- , cnt= ; cnt<len ; cnt++ , i++ , j--){
// cout<<cnt<<" "<<i<<" "<<j<<" "<<str[i]<<" "<<str[j]<<endl;
if(str[i]>str[j]){wise=;pos=ret1;break;}
else if(str[i]<str[j]){wise=;pos=ret2;break;}
}
if(wise==-){
if(ret1<ret2) pos = ret1 , wise = ;
else if(ret1>ret2) pos = ret2 , wise = ;
else pos=ret1 , wise=;
// cout<<"in: "<<endl;
}
printf("%d %d\n" , pos, wise);
}
return ;
}
SAM+kmp
后来终于受人指点知道了直接在后缀自动机上找到最后一个位置得到的最大字典序怎么求了,这样就不用求kmp了
后缀自动机上s记录达到的最长的位置,如果不更新,那么必然一次跑完得到的是最小位置,那么为了得到最大,之前更新每一个节点中的s,只有儿子位置上的能更新父亲
所以先将后缀自动机拓扑排序,然后从后往前,不断将父亲的s更新成更大的s
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define M 26
#define N 80100
#define ull unsigned long long
char str[N] , tmp[N];
int n , num[N];
queue<int> Q; struct SamNode{
SamNode *son[M] , *f;
int l , s;
void init(){
for(int i= ; i<M ; i++) son[i] = NULL;
f = NULL;
l = s = ;
}
}*b[N]; SamNode sam[N] , *root , *last;
int cnt; void init(){
cnt = ;
sam[].init();
root = last = &sam[];
} void add(int x){
sam[cnt+].init();
SamNode *p = &sam[++cnt] , *jp=last;
/*
这里p->s = jp->s+1写成p->s = p->l也是一样的,因为对于每次当前的last来说,
l和s的值是一样的,因为每次当前的last都是处于字符串的位置上的
数,不是额外添加的节点
*/
p->l = jp->l+; p->s = jp->s+;
last = p;
for(; jp&&!jp->son[x] ; jp=jp->f) jp->son[x] = p;
if(!jp) p->f=root;
else{
if(jp->l+ == jp->son[x]->l) p->f = jp->son[x];
else{
sam[cnt+].init();
SamNode *r = &sam[++cnt] , *q = jp->son[x];
*r=*q;
r->l = jp->l+ ; r->s = p->l;
q->f = p->f = r;
for(; jp && jp->son[x]==q ; jp=jp->f) jp->son[x]=r;
}
}
} int solve(int len)
{
SamNode *cur = root;
for(int i= ; i<len ; i++){
for(int j= ; j>= ; j--){
if(cur->son[j]){
cur = cur->son[j];
break;
}
}
}
int ret = cur->s-len+; return ret;
} int solve1(int len)
{
memset(num , , sizeof(num));
for(int i= ; i<=cnt ; i++) num[sam[i].l]++;
for(int i= ; i<=len* ; i++) num[i] += num[i-];
for(int i= ; i<=cnt ; i++) b[--num[sam[i].l]] = &sam[i];
for(int i=cnt- ; i>= ; i--){
b[i]->f->s = max(b[i]->f->s , b[i]->s);
}
SamNode *cur = root;
for(int i= ; i<len ; i++){
for(int j= ; j>= ; j--){
if(cur->son[j]){
cur = cur->son[j];
break;
}
}
}
int ret = cur->s-len+;
return ret;
} int main() {
// freopen("a.in" , "r" , stdin);
// freopen("out.txt" , "w" , stdout);
int T;
scanf("%d" , &T);
while(T--){
scanf("%d" , &n);
scanf("%s" , str);
int len = n;
init();
for(int i= ; i<len ; i++)
str[len+i] = str[i];
for(int i= ; i<len* ; i++)
add(str[i]-'a');
// for(int i=0 ; i<2*n ; i++) cout<<str[i];
//cout<<endl;
int ret1 = solve(len); for(int i= ; i<len ; i++){
tmp[len-i-] = str[i];
}
for(int i= ; i<len ; i++){
tmp[len+i] = tmp[i];
} init();
for(int i= ; i<len*- ; i++)
add(tmp[i]-'a');
int ret2 = len-solve1(len)+; // cout<<"here: "<<ret1<<" "<<ret2<<endl;
// cout<<ret1<<" "<<ret2<<endl;
int i , j , cnt , pos , wise=-;
for(i=ret1- , j=ret2+len- , cnt= ; cnt<len ; cnt++ , i++ , j--){
// cout<<cnt<<" "<<i<<" "<<j<<" "<<str[i]<<" "<<str[j]<<endl;
if(str[i]>str[j]){wise=;pos=ret1;break;}
else if(str[i]<str[j]){wise=;pos=ret2;break;}
}
if(wise==-){
if(ret1<ret2) pos = ret1 , wise = ;
else if(ret1>ret2) pos = ret2 , wise = ;
else pos=ret1 , wise=;
// cout<<"in: "<<endl;
}
printf("%d %d\n" , pos, wise);
}
return ;
}
HDU 5442 后缀自动机+kmp的更多相关文章
- HDU 5442 后缀自动机(从环字符串选定一个位置 , 时针或顺时针走一遍,希望得到字典序最大)
http://acm.hdu.edu.cn/showproblem.php?pid=5442 题目大意: 给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样 ...
- HDU 4436 (后缀自动机)
HDU 4436 str2int Problem : 给若干个数字串,询问这些串的所有本质不同的子串转换成数字之后的和. Solution : 首先将所有串丢进一个后缀自动机.由于这道题询问的是不同的 ...
- HDU 4622 (后缀自动机)
HDU 4622 Reincarnation Problem : 给一个串S(n <= 2000), 有Q个询问(q <= 10000),每次询问一个区间内本质不同的串的个数. Solut ...
- HDU 4416 (后缀自动机)
HDU 4416 Good Article Good sentence Problem : 给一个串S,和一些串T,询问S中有多少个子串没有在T中出现. Solution :首先对所有的T串建立后缀自 ...
- [BZOJ3796]Mushroom追妹纸:后缀自动机+KMP
分析 这道题有个\(O(n)\)的后缀自动机做法,感觉很好理解就在这说一下. 先对\(s1\)和\(s2\)求最长公共子串,对于\(s2\)的每一个下标\(i\),求一个\(f[i]\)表示以\(s2 ...
- hdu 6208(后缀自动机、或者AC自动机
题意:给你n个字符串,问你是否存在一个字符串可以从中找到其他n-1个字符串. 思路:其实很简单,找到最长的那个字符串对他进行匹配,看是否能匹配到n-1个字符串. 可以用AC自动机或者后缀自动机做,但是 ...
- Boring counting HDU - 3518 后缀自动机
题意: 对于给出的字符串S, 长度不超过1000, 求其中本质不同的子串的数量, 这些子串满足在字符串S中出现了至少不重合的2次 题解: 将串放入后缀自动机中然后求出每一个节点对应的子串为后缀的子串出 ...
- Alice's Classified Message HDU - 5558 后缀自动机求某个后缀出现的最早位置
题意: 给定一个长度不超过 10W 的只包含小写字母的字符串,从下标 0 到 n−1.从下标 0 开始操作, 每次对于下标 pos查找下标 pos 开始的子串中最长的在其他地方出现过的长度,其他出现的 ...
- str2int HDU - 4436 后缀自动机求子串信息
题意: 给出 n 个串,求出这 n 个串所有子串代表的数字的和. 题解; 首先可以把这些串构建后缀自动机(sam.last=1就好了), 因为后缀自动机上从 root走到的任意节点都是一个子串,所有可 ...
随机推荐
- JSP中RequestDispatcher的用法
RequestDispatcher是一个Web资源的包装器,可以用来把当前request传递到该资源,或者把新的资源包括到当前响应中.RequestDispatcher接口中定义了两个方法:inclu ...
- iphone SE 自带视频播放器要求的视频格式转换参数
- hiho_1054_滑动解锁
题目大意 智能手机九点屏幕滑动解锁,如果给出某些连接线段,求出经过所有给出线段的合法的滑动解锁手势的总数.题目链接: 滑动解锁 题目分析 首先,尝试求解没有给定线段情况下,所有合法的路径的总数.可以使 ...
- C/C++中float和double的存储结构
int main (int argc, char **argv) { float a = 1.0f; cout <<"(int&)a = "<<(i ...
- ubuntu server nginx 安装与配置
ubuntu server nginx 安装与配置 一:关于nginx http://wiki.ubuntu.org.cn/Nginx http://nginx.org/cn http://wiki. ...
- 009-Selenium2环境搭建
1.Java开发环境的搭建 本课程中将使用Java语言编写Selenium自动化测试脚本,在Eclipse集成开发环境中运行. (1)jdk的安装 a.下载 官网下载,http://www. ...
- js数据类型和关系运算语法
var box=; alert(typeof box); //box是Undefined类型,值是undefined,类型返回的字符串是undefined var box=true; alert(ty ...
- servlet和web容器之间的关系
Java是一种动态加载和运行的语言.也就是说当应用程序持有一个类的地址(CLASSPATH)和名称(包名和类名)的情况下,可以在程序运行期 间任何时候加载这个类,并创建和使用该类的对象.Servlet ...
- JavaScript基础知识(1)
表单的确认 : 客户端确认 --减少服务器负载 --缩短用户等待时间 --兼容性难 服务器端确认: ----统一确认 ----兼容性强 ----服务器负载重 JavaScript基本的写法: ...
- 张艾迪(创始人):发明Global.World.224C的天才
Eidyzhang: Genius.Founder.CEO.23 I 世界级最高级创始人.世界最高级FounderCEO 出生在亚洲中国.Eidyzhang 拥有黑色头发白色皮肤(20岁)大学辍学生. ...