2016 China-Final-F题 ——(SA+二分)
其实是一个很经典的字符串问题,但是我们比赛的时候没出。
先看一下UVA11107这题,题意是,找出最长的一个字符串,在至少一半的字符串中出现过。只要把所有的字符串用不同的分隔符分开,然后SA一下,最后二分长度,用height将字符串分组,判断是否超过一半即可。要注意的是,因为分隔符单单用个char已经不够了,所以全部char都换成int,然后用不同的整数来作为分隔符即可。
代码如下:
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
const int N = + ;
typedef long long ll;
const int sep = 'z' + ; /**
* sa[i]:表示排在第i位的后缀的起始下标
* rank[i]:表示后缀suffix(i)排在第几
* height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
*
* */
/*
如果整数的话模板改成int.
加一个数a[n] = 0 。 这样他的排名是第一个。
construct(a,n+1); 字符串的话。
len = strlen(str);
construct(s,strlen(s)+1);
排名第0的是个空字符串。 height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
所以height[1] = 0;
rank[len] = 0;
sa[0] = len;
*/
int sa[N],rnk[N],height[N];
void construct(const int *s,int n,int m = ) {
static int t1[N],t2[N],c[N];
int *x = t1,*y = t2;
int i,j,k,p,l;
for (i = ; i < m; ++ i) c[i] = ;
for (i = ; i < n; ++ i) c[x[i] = s[i]] ++;
for (i = ; i < m; ++ i) c[i] += c[i - ];
for (i = n - ; i >= ; -- i) sa[--c[x[i]]] = i;
for (k = ; k <= n; k <<= ) {
p = ;
for (i = n - k; i < n; ++ i) y[p++] = i;
for (i = ; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k;
for (i = ; i < m; ++ i) c[i] = ;
for (i = ; i < n; ++ i) c[x[y[i]]] ++;
for (i = ; i < m; ++ i) c[i] += c[i - ];
for (i = n - ; i >= ; -- i) sa[--c[x[y[i]]]] = y[i];
std::swap(x,y);
p = ; x[sa[]] = ;
for (i = ; i < n; ++ i)
x[sa[i]] = y[sa[i - ]] == y[sa[i]]
&& y[sa[i - ] + k] == y[sa[i] + k] ? p - : p ++;
if (p >= n) break;
m = p;
}
for (i = ; i < n; ++ i) rnk[sa[i]] = i;
for (i = ,l = ; i < n; ++ i) {
if (rnk[i]) {
j = sa[rnk[i] - ];
while (s[i + l] == s[j + l]) l++;
height[rnk[i]] = l;
if (l) l--;
}
}
} char str[];
int s[N];
int End[],len,n;
bool vis[];
vector<int> ans; bool solve(int Len)
{
ans.clear();
int cnt = ;
memset(vis,false,sizeof(vis));
for(int i=;i<=len;i++)
{
if(height[i] >= Len)
{
for(int j=;j<=n;j++)
{
if(sa[i] > End[j-] && sa[i] < End[j]) {cnt += !vis[j]; vis[j] = ;}
if(sa[i-] > End[j-] && sa[i-] < End[j]) {cnt += !vis[j]; vis[j] = ;}
}
}
else
{
if(cnt > n / ) ans.push_back(sa[i-]);
cnt = ;
memset(vis,false,sizeof(vis));
}
}
if(cnt > n / ) ans.push_back(sa[len]);
return ans.size();
} int main()
{
int first = ;
while(scanf("%d",&n) == && n)
{
if(first == ) first = ;
else puts("");
len = ;
for(int i=;i<=n;i++)
{
scanf("%s",str);
for(int j=;str[j];j++) s[len++] = str[j]; s[len++] = sep + i;
End[i] = len-;
}
s[len] = ;
construct(s,len+);
int l = , r = len, Ans = -;
while(l <= r)
{
int mid = l + r >> ;
if(solve(mid)) l = mid + , Ans = mid;
else r = mid - ;
} if(Ans == -) puts("?");
else
{
// 为了得到答案,再solve一遍
solve(Ans);
for(int i=;i<ans.size();i++)
{
for(int j=ans[i],cnt=;cnt<=Ans;j++,cnt++) putchar(s[j]);
puts("");
}
//puts("");
}
}
return ;
}
然后看下F题,题意是,找出最短的一个字符串,只在第一个字符串中出现。那么,一样的套路:全部连接,然后SA一遍,然后分组,找这一组中是不是都只是在第一个字符串中出现(这里采用了一个belong数组来判断每个字符是属于哪个串中的)。
代码如下:
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
const int N = 3e6+;
const int M = 5e4+;
typedef long long ll;
const int sep = 'z' + ; /**
* sa[i]:表示排在第i位的后缀的起始下标
* rank[i]:表示后缀suffix(i)排在第几
* height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
*
* */
/*
如果整数的话模板改成int.
加一个数a[n] = 0 。 这样他的排名是第一个。
construct(a,n+1); 字符串的话。
len = strlen(str);
construct(s,strlen(s)+1);
排名第0的是个空字符串。 height[i]:sa[i-1] 与 sa[i]的LCP(最长公共前缀)值
所以height[1] = 0;
rank[len] = 0;
sa[0] = len;
*/
int sa[N],rnk[N],height[N];
void construct(const int *s,int n,int m = ) {
static int t1[N],t2[N],c[N];
int *x = t1,*y = t2;
int i,j,k,p,l;
for (i = ; i < m; ++ i) c[i] = ;
for (i = ; i < n; ++ i) c[x[i] = s[i]] ++;
for (i = ; i < m; ++ i) c[i] += c[i - ];
for (i = n - ; i >= ; -- i) sa[--c[x[i]]] = i;
for (k = ; k <= n; k <<= ) {
p = ;
for (i = n - k; i < n; ++ i) y[p++] = i;
for (i = ; i < n; ++ i) if (sa[i] >= k) y[p++] = sa[i] - k;
for (i = ; i < m; ++ i) c[i] = ;
for (i = ; i < n; ++ i) c[x[y[i]]] ++;
for (i = ; i < m; ++ i) c[i] += c[i - ];
for (i = n - ; i >= ; -- i) sa[--c[x[y[i]]]] = y[i];
std::swap(x,y);
p = ; x[sa[]] = ;
for (i = ; i < n; ++ i)
x[sa[i]] = y[sa[i - ]] == y[sa[i]]
&& y[sa[i - ] + k] == y[sa[i] + k] ? p - : p ++;
if (p >= n) break;
m = p;
}
for (i = ; i < n; ++ i) rnk[sa[i]] = i;
for (i = ,l = ; i < n; ++ i) {
if (rnk[i]) {
j = sa[rnk[i] - ];
while (s[i + l] == s[j + l]) l++;
height[rnk[i]] = l;
if (l) l--;
}
}
} char str[N];
int s[N];
int endlen[N],len,n,belong[N];
char temp[N];
int all;
int from; bool check(int st,int ed,int L)
{
int now = -;
for(int i=st;i<=ed;i++)
{
if(belong[sa[i]] == && endlen[sa[i]] >= L)
{
if(now == -) now = sa[i];
continue;
}
return false;
}
from = now;
return true;
} bool solve(int L)
{
int st = , ed = ;
for(int i=;i<all;i++)
{
if(height[i] >= L) ed++;
else
{
if(check(st,ed,L)) return ;
st = ed = i;
}
}
return check(st,ed,L);
} int main()
{
int T, kase = ;
scanf("%d",&T);
while(T--)
{
printf("Case #%d: ",kase++);
scanf("%d%s",&n,temp);
int now_len = strlen(temp);
len = ;
for(int i=;temp[i];i++)
{
s[len] = temp[i];
belong[len] = ;
endlen[len] = now_len - i;
len ++;
}
s[len] = sep + ; belong[len] = , endlen[len] = ;
int first_len = len ++;
for(int i=;i<=n;i++)
{
scanf("%s",str);
int now_len = strlen(str);
for(int j=;j<now_len;j++)
{
s[len] = str[j];
belong[len] = i;
endlen[len] = now_len - j;
len ++;
}
s[len] = sep + i; belong[len] = i, endlen[len] = ; len++;
}
s[len] = ;
construct(s,len+, + );
all = rnk[first_len];
int l = , r = first_len, ans = -;
while(l <= r)
{
int mid = l + r >> ;
if(solve(mid)) r = mid - , ans = mid;
else l = mid + ;
}
if(ans == -) puts("Impossible");
else
{
for(int i=from;i<from+ans;i++) printf("%c",temp[i]);
puts("");
}
}
return ;
}
感觉二分+SA就是一种套路,可以结合前几天做的那题一起看看,最少出现m次的最长字符串。
2016 China-Final-F题 ——(SA+二分)的更多相关文章
- Northwestern European Regional Contest 2016 NWERC ,F题Free Weights(优先队列+Map标记+模拟)
传送门: Vjudge:https://vjudge.net/problem/Gym-101170F CF: http://codeforces.com/gym/101170 The city of ...
- ICPC 2016 China Final J. Mr.Panda and TubeMaster【最大费用最大流】
有一种限制下界强制选的,但是也可以不用 把每个格点拆成两个,一个连s一个连t,对于不是必选的连中间连流量1费用0边表示不选,然后黑白染色,黑点连横着白点连竖着,边权就是这条水管的权值,然后跑最大费用最 ...
- 【费用流】 ICPC 2016 China Final J. Mr.Panda and TubeMaster
表示“必须选”的模型 题目大意 题目分析 一个格子有四种方式看上去很难处理.将横竖两个方向分开考虑,会发现:因为收益只与相邻格子是否连通有关,所以可以将一个格子拆成表示横竖两个方向的,互相独立的点. ...
- 2016 China Final E - Bet
/************************************************************************* > File Name: E.cpp > ...
- 2016 China Final H - Great Cells
/************************************************************************* > File Name: H.cpp > ...
- 2016 China Collegiate Programming Contest Final
2016 China Collegiate Programming Contest Final Table of Contents 2016 China Collegiate Programming ...
- ACM ICPC China final G Pandaria
目录 ACM ICPC China final G Pandaria ACM ICPC China final G Pandaria 题意:给一张\(n\)个点\(m\)条边的无向图,\(c[i]\) ...
- Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution
Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution 题目链接:https://atcoder.jp/contests/cf16- ...
- 2017Summmer_上海金马五校 F题,G题,I题,K题,J题
以下题目均自己搜 F题 A序列 一开始真的没懂题目什么意思,还以为是要连续的子串,结果发现时序列,简直智障,知道题意之后,好久没搞LIS,有点忘了,复习一波以后,直接双向LIS,处理处两个数组L和R ...
随机推荐
- 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁
1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...
- Visual Studio 设置多核编译
1.选择一个指定项目右击 -> 属性 -> 配置属性 -> C/C++ -> 命令行 在附加选项中输入:/MP4 或者/MP8 等,后面的那个数字是指定CPU 核的数量,可以自 ...
- DELL PowerEdge 2950更换告警硬盘
硬盘为SAS300G15K,四块,3#告警,打算还掉,在R900上找到一块对应的硬盘直接换下. 进入控制台后发现硬盘阵列里还是只有三块硬盘,物理磁盘倒是有四块,新插上的一块状态为“外部”,其他状态是“ ...
- Unity3D优化总结
1.在使用数组或ArrayList对象时应当注意 length=myArray.Length; for(int i=0;i<length;i++) { } 避免 for(int i=0;i< ...
- Erlang初学
这篇文章主要介绍了Erlang初学:Erlang的一些特点和个人理解总结,本文总结了函数式编程.一切都是常量.轻量进程.进程端口映射及典型缺点等内容,需要的朋友可以参考下 我对 Erlang 编程理念 ...
- Ubuntu16.04 apache2 wsgi 部署django
在Ubuntu16.04上部署django其实还算简单直观,最重要的问题就是路径设置正确,并且保证版本统一,这个测试是在 Apache/2.4.18 (Ubuntu) apt-get install ...
- 自动化测试 using System.Windows.Automation;
frameworke3.0 及以上 using System.Windows.Automation; UIAutomationClient.dll UIAutomationClientsideProv ...
- docker理念:不可变基础设施
不可变基础设施 1.什么是Immutable Infrastructure Immutable Infrastructure,直译过来就是不可变基础设施. 它是由Chad Fowler于2013年提出 ...
- 利用Flex组件birdeye绘制拓扑关系图
birdeye绘制拓扑关系图 1.flex简单介绍 Flex 是一个高效.免费的开源框架,可用于构建具有表现力的 Web应用程序,这些应用程序利用Adobe Flash Player和Adobe AI ...
- HTTP 2.0的那些事
转自:http://www.admin10000.com/document/9310.html 在我们所处的互联网世界中,HTTP协议算得上是使用最广泛的网络协议.最近http2.0的诞生使得它再次互 ...