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 ...
随机推荐
- java 泛型编程学习
先发布,以后有空再修改... 第一次看到<java核心技术卷一>中关于泛型这部分的时候感觉很复杂,似乎有说不完的约束条件,让人难以理解.当时只是囫囵吞枣般过了一遍,也没有看出个什么来.现在 ...
- bootstrap-滚动监听
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- Ajaxtoolkit Combobox位置显示不对解决
当父级容器设置positon是absolute或relative,combobox显示的就会出现偏移,解决办法如下: 添加这个样式 .ajax__combobox_itemlist{ posit ...
- SQL语句学习手册实例版
SQL语句学习手册实例版 表操作 例1 对于表的教学管理数据库中的表 STUDENTS ,可以定义如下: CREATE TABLE STUDENTS (SNO NUMERIC (6, ...
- C# 访问https 未能创建 SSL/TLS 安全通道
C# 访问https请求被中止: 未能创建 SSL/TLS 安全通道(Could not create SSL/TLS secure channel) 一般GetResponse可以直接访问https ...
- 可分组的选择框控件(MVVM下)(Toggle样式 仿造单选框RadioButton,复选框CheckBox功能)
原地址: http://www.cnblogs.com/yk250/p/5660340.html 效果图如下:支持分组的单选框,复选框样式和MVVM下功能的实现.这是项目中一个快捷键功能的扩展. 1, ...
- Git self-learning
---恢复内容开始--- 使用后的总结 git config --global user.name "" #设置和查看用户名git config --global user.ema ...
- (Hibernate进阶)Hibernate系列——总结篇(九)
这篇博文是hibernate系列的最后一篇,既然是最后一篇,我们就应该进行一下从头到尾,整体上的总结,将这个系列的内容融会贯通. 概念 Hibernate是一个对象关系映射框架,当然从分层的角度看,我 ...
- sqlldr
1.字符集 sqlldr可以指定读取的文件的字符集,如果数据库为gbk,读取的文件为utf-8,这个时候就需要指定字符集 load data CHARACTERSET 'UTF8' 2.sqlldr导 ...
- Java多线程Thread
转自:http://www.cnblogs.com/lwbqqyumidi/p/3804883.html Java总结篇系列:Java多线程(一) 多线程作为Java中很重要的一个知识点,在此还是 ...