hdu 5442 (ACM-ICPC2015长春网络赛F题)
题意:给出一个字符串,长度是2*10^4。将它首尾相接形成环,并在环上找一个起始点顺时针或逆时针走一圈,求字典序最大的走法,如果有多个答案则找起始点最小的,若起始点也相同则选择顺时针。
分析:后缀数组。
首先,需要补充后缀数组的基本知识的话,看这个链接。
http://www.cnblogs.com/rainydays/archive/2011/05/09/2040993.html
我利用这道题重新整理了后缀数组的模板如下:
const int MAX_LEN = ;
//call init_RMQ(f[], n) first.
//then call query(a, b) to quest the RMQ of [a, b].
int power[];
int st[MAX_LEN * ][];
int ln[MAX_LEN * ]; //returns the index of the first minimum value in [x, y]
void init_RMQ(int f[], int n)
{
int i, j;
for (power[] = , i = ; i < ; i++)
{
power[i] = * power[i - ];
}
for (i = ; i < n; i++)
{
st[i][] = i;
}
ln[] = -;
for (int i = ; i <= n; i++)
{
ln[i] = ln[i >> ] + ;
}
for (j = ; j < ln[n]; j++)
{
for (i = ; i < n; i++)
{
if (i + power[j - ] - >= n)
{
break;
}
//for maximum, change ">" to "<"
//for the last, change "<" or ">" to "<=" or ">="
if (f[st[i][j - ]] > f[st[i + power[j - ]][j - ]])
{
st[i][j] = st[i + power[j - ]][j - ];
}
else
{
st[i][j] = st[i][j - ];
}
}
}
} int query(int f[], int x, int y)
{
if(x > y)
{
swap(x, y);
}
int k = ln[y - x + ];
//for maximum, change ">" to "<"
//for the last, change "<" or ">" to "<=" or ">="
if (f[st[x][k]] > f[st[y - power[k] + ][k]])
return st[y - power[k] + ][k];
return st[x][k];
} //first use the constructed function
//call function lcp(l, r) to get the longest common prefix of sa[l] and sa[r]
//have access to the member of sa, myrank, height and so on
//height is the LCP of sa[i] and sa[i + 1]
class SuffixArray
{
public:
char* s;
int n, sa[MAX_LEN], height[MAX_LEN], myrank[MAX_LEN];
int tmp[MAX_LEN], top[MAX_LEN]; SuffixArray()
{} //the string and the length of the string
SuffixArray(char* st, int len)
{
s = st;
n = len + ;
make_sa();
make_lcp();
} void make_sa()
{
// O(N * log N)
int na = (n < ? : n);
memset(top, , na * sizeof(int));
for (int i = ; i < n ; i++)
top[myrank[i] = s[i] & 0xff]++;
for (int i = ; i < na; i++)
top[i] += top[i - ];
for (int i = ; i < n ; i++)
sa[--top[ myrank[i]]] = i;
int x;
for (int len = ; len < n; len <<= )
{
for (int i = ; i < n; i++)
{
x = sa[i] - len;
if (x < )
x += n;
tmp[top[myrank[x]]++] = x;
}
sa[tmp[top[] = ]] = x = ;
for (int i = ; i < n; i++)
{
if (myrank[tmp[i]] != myrank[tmp[i-]] ||
myrank[tmp[i]+len]!=myrank[tmp[i-]+len])
top[++x] = i;
sa[tmp[i]] = x;
}
memcpy(myrank, sa , n * sizeof(int));
memcpy(sa , tmp, n * sizeof(int));
if (x >= n - )
break;
}
} void make_lcp()
{
// O(4 * N)
int i, j, k;
for (j = myrank[height[i = k = ] = ]; i < n - ; i++, k++)
{
while (k >= && s[i] != s[sa[j - ] + k])
{
height[j - ] = (k--);
j = myrank[sa[j] + ];
}
}
init_RMQ(height, n - );
} int lcp(int l, int r)
{
return height[query(height, l, r - )];
}
};
先将两个原字符串拼成一个长度是2倍的字符串,便于处理越过原串末尾的情况。
运行后缀数组求顺时针解。
反转这个长字符串,再运行后缀数组,求逆时针解。
比较两个解,得出最终解。
下面我们来分析一下,每次运行后缀数组之后是如何求解的。
设原串长度为len。
首先从sa[len*2]开始依次向前找。即从排序最大的开始向前找。之所以排序最大的不一定是我们要的答案是因为:
1.它可能起始点在后面的附加的串上,并没有构成完整的一圈。
2.我们还要找起始点最小的答案呢。(当然,对于反转后的字符串,我们要找起始点最大的)
在第一次遇到一个起始点小于len的时候,表明这个可能就是答案。但是前面可能有起始点更小的,我们还要继续沿着sa向前找,但找的时候一定要保证字典序是最大的,所以每次向前移动一个,都要观察height(i, i+1)是否>len,如果不是,则说明字典序已经不是最大了。
特别注意,height==len的情况也是不可以的,因为两者相等很有可能表明其中一个的起始点已经等于len。当然也可以对每个都判断一下起始点位置。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; #define d(x) const int MAX_N = (int)(4e4) + ; //call init_RMQ(f[], n) first.
//then call query(a, b) to quest the RMQ of [a, b].
int power[];
int st[MAX_N * ][];
int ln[MAX_N * ]; //returns the index of the first minimum value in [x, y]
void init_RMQ(int f[], int n)
{
int i, j;
for (power[] = , i = ; i < ; i++)
{
power[i] = * power[i - ];
}
for (i = ; i < n; i++)
{
st[i][] = i;
}
ln[] = -;
for (int i = ; i <= n; i++)
{
ln[i] = ln[i >> ] + ;
}
for (j = ; j < ln[n]; j++)
{
for (i = ; i < n; i++)
{
if (i + power[j - ] - >= n)
{
break;
}
//for maximum, change ">" to "<"
//for the last, change "<" or ">" to "<=" or ">="
if (f[st[i][j - ]] > f[st[i + power[j - ]][j - ]])
{
st[i][j] = st[i + power[j - ]][j - ];
}
else
{
st[i][j] = st[i][j - ];
}
}
}
} int query(int f[], int x, int y)
{
if(x > y)
{
swap(x, y);
}
int k = ln[y - x + ];
//for maximum, change ">" to "<"
//for the last, change "<" or ">" to "<=" or ">="
if (f[st[x][k]] > f[st[y - power[k] + ][k]])
return st[y - power[k] + ][k];
return st[x][k];
} //first use the constructed function
//call function lcp(l, r) to get the longest common prefix of sa[l] and sa[r]
//have access to the member of sa, myrank, height and so on
//height is the LCP of sa[i] and sa[i + 1]
class SuffixArray
{
public:
char* s;
int n, sa[MAX_N], height[MAX_N], myrank[MAX_N];
int tmp[MAX_N], top[MAX_N]; SuffixArray()
{} //the string and the length of the string
SuffixArray(char* st, int len)
{
s = st;
n = len + ;
make_sa();
make_lcp();
} void make_sa()
{
// O(N * log N)
int na = (n < ? : n);
memset(top, , na * sizeof(int));
for (int i = ; i < n ; i++)
top[myrank[i] = s[i] & 0xff]++;
for (int i = ; i < na; i++)
top[i] += top[i - ];
for (int i = ; i < n ; i++)
sa[--top[ myrank[i]]] = i;
int x;
for (int len = ; len < n; len <<= )
{
for (int i = ; i < n; i++)
{
x = sa[i] - len;
if (x < )
x += n;
tmp[top[myrank[x]]++] = x;
}
sa[tmp[top[] = ]] = x = ;
for (int i = ; i < n; i++)
{
if (myrank[tmp[i]] != myrank[tmp[i-]] ||
myrank[tmp[i]+len]!=myrank[tmp[i-]+len])
top[++x] = i;
sa[tmp[i]] = x;
}
memcpy(myrank, sa , n * sizeof(int));
memcpy(sa , tmp, n * sizeof(int));
if (x >= n - )
break;
}
} void make_lcp()
{
// O(4 * N)
int i, j, k;
for (j = myrank[height[i = k = ] = ]; i < n - ; i++, k++)
{
while (k >= && s[i] != s[sa[j - ] + k])
{
height[j - ] = (k--);
j = myrank[sa[j] + ];
}
}
init_RMQ(height, n - );
} int lcp(int l, int r)
{
return height[query(height, l, r - )];
}
}; SuffixArray suffix;
int len;
char ans1[MAX_N], ans2[MAX_N];
int pos1, pos2;
char s[MAX_N]; int work1(char* ans)
{
int p = len * ;
while (suffix.sa[p] >= len)
p--;
for (int i = p - ; i >= ; i--)
{
if (suffix.lcp(i, i + ) <= len)
break;
if (suffix.sa[i] < suffix.sa[p])
{
p = i;
}
}
memcpy(ans, s + suffix.sa[p], len);
ans[len] = ;
return suffix.sa[p];
} int work2(char* ans)
{
int p = len * ;
while (suffix.sa[p] >= len)
p--;
for (int i = p - ; i >= ; i--)
{
if (suffix.lcp(i, i + ) <= len)
break;
if (suffix.sa[i] > suffix.sa[p])
{
p = i;
}
}
memcpy(ans, s + suffix.sa[p], len);
ans[len] = ;
return suffix.sa[p];
} int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d", &len);
scanf("%s", s);
for (int i = ; i < len; i++)
{
s[len + i] = s[i];
}
suffix = SuffixArray(s, len * );
pos1 = work1(ans1);
reverse(s, s + len * );
suffix = SuffixArray(s, len * );
pos2 = work2(ans2);
pos2 = len - - pos2; if (strcmp(ans1, ans2) > )
{
printf("%d 0\n", pos1 + );
continue;
}else if (strcmp(ans1, ans2) < )
{
printf("%d 1\n", pos2 + );
continue;
}else if (pos1 <= pos2)
{
printf("%d 0\n", pos1 + );
continue;
}else
{
printf("%d 1\n", pos2 + );
}
}
return ;
}
hdu 5442 (ACM-ICPC2015长春网络赛F题)的更多相关文章
- 2013 ACM/ICPC 长春网络赛F题
题意:两个人轮流说数字,第一个人可以说区间[1~k]中的一个,之后每次每人都可以说一个比前一个人所说数字大一点的数字,相邻两次数字只差在区间[1~k].谁先>=N,谁输.问最后是第一个人赢还是第 ...
- hdu 5441 (2015长春网络赛E题 带权并查集 )
n个结点,m条边,权值是 从u到v所花的时间 ,每次询问会给一个时间,权值比 询问值小的边就可以走 从u到v 和从v到u算不同的两次 输出有多少种不同的走法(大概是这个意思吧)先把边的权值 从小到大排 ...
- 2013 ACM/ICPC 长春网络赛E题
题意:给出一个字符串,要从头.尾和中间找出三个完全相等的子串,这些串覆盖的区间互相不能有重叠部分.头.尾的串即为整个字符串的前缀和后缀.问这个相同的子串的最大长度是多少. 分析:利用KMP算法中的ne ...
- 2013 ACM/ICPC 南京网络赛F题
题意:给出一个4×4的点阵,连接相邻点可以构成一个九宫格,每个小格边长为1.从没有边的点阵开始,两人轮流向点阵中加边,如果加入的边构成了新的边长为1的小正方形,则加边的人得分.构成几个得几分,最终完成 ...
- ACM-ICPC 2019南昌网络赛F题 Megumi With String
ACM-ICPC 南昌网络赛F题 Megumi With String 题目描述 给一个长度为\(l\)的字符串\(S\),和关于\(x\)的\(k\)次多项式\(G[x]\).当一个字符串\(str ...
- HDU 4764 Stone (2013长春网络赛,水博弈)
Stone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- HDU 4762 Cut the Cake (2013长春网络赛1004题,公式题)
Cut the Cake Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tota ...
- HDU 4759 Poker Shuffle(2013长春网络赛1001题)
Poker Shuffle Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- HDU 4768 Flyer (2013长春网络赛1010题,二分)
Flyer Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
随机推荐
- [c#]RabbitMQ的简单使用
摘要 Message Queue消息队列,简称MQ,是一种应用程序对应用程序的通信方法,应用程序通过读写出入队列的消息来通信,而无需专用连接来链接它们.消息传递指的是程序之间通过在消息中发送数据进行通 ...
- 【最新】2015年7月之15个最新jQuery插件
Hello,一个激动人心的好消息,现在我为大家整理最近7月发布的jQuery插件. 如果你熟悉任何下面列出的插件,请分享你的反馈与我们的读者,或如果你知道哪一个我们没有收录,那么请与我们分享在下面的评 ...
- Python XML解析(转载)
Python XML解析 什么是XML? XML 指可扩展标记语言(eXtensible Markup Language). 你可以通过本站学习XML教程 XML 被设计用来传输和存储数据. XML是 ...
- Windows Phone 开发环境的搭建
1. 系统 系统:Windows 7(32 位).Windows 7(64 位).Windows Vista SP2(32 位)和 Windows Vista(64 位)或者更高版本. 不支持 :Wi ...
- POJ 2299 Ultra-QuickSort
离散化+树状数组求逆序数 Ultra-QuickSort Time Limit: 7000MS Memory Limit: 65536K Total Submissions: 35024 Accept ...
- VC++6.0MFC运行的简单流程
背景: 由于下位机做的一些东西,总是需要通过上位机来验证,以及为了直观给客户展示下位机的功能,所以时常需要编写一些简单的APP.今天就以VC++6.0为例,简单的记录下该如何快速的创建一个APP. 正 ...
- hadoop 集群 加入一个新的存储节点和删除一个计算节点需要刷新集群状态命令
加入一个新的存储节点和删除一个计算节点需要刷新集群状态命令 方式1:静态添加datanode,停止namenode方式 1.停止namenode 2.修改slaves文件,并更新到各个节点3.启动na ...
- 【PHP面向对象(OOP)编程入门教程】8.构造方法__construct()与析构方法__destruct()
大多数类都有一种称为构造函数的特殊方法.当创建一个对象时,它将自动调用构造函数,也就是使用new这个关键字来实例化对象的时候自动调用构造方法.构 造函数的声明与其它操作的声明一样,只是其名称必须是__ ...
- PHP的$_SERVER['PHP_SELF']造成的XSS漏洞攻击及其解决方案
$_SERVER['PHP_SELF']简介 $_SERVER['PHP_SELF'] 表示当前 PHP文件相对于网站根目录的位置地址,与 document root 相关. 假设我们有如下网址,$_ ...
- PPPoE名词解释
PPPoE拔号的发现阶段(Discovery): PPPoE的发现阶段一共分为4步. 分别是: PADI(PPPoE Active Discovery Initiation) PADO(PPPoE A ...