其实是一个很经典的字符串问题,但是我们比赛的时候没出。

  先看一下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+二分)的更多相关文章

  1. 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 ...

  2. ICPC 2016 China Final J. Mr.Panda and TubeMaster【最大费用最大流】

    有一种限制下界强制选的,但是也可以不用 把每个格点拆成两个,一个连s一个连t,对于不是必选的连中间连流量1费用0边表示不选,然后黑白染色,黑点连横着白点连竖着,边权就是这条水管的权值,然后跑最大费用最 ...

  3. 【费用流】 ICPC 2016 China Final J. Mr.Panda and TubeMaster

    表示“必须选”的模型 题目大意 题目分析 一个格子有四种方式看上去很难处理.将横竖两个方向分开考虑,会发现:因为收益只与相邻格子是否连通有关,所以可以将一个格子拆成表示横竖两个方向的,互相独立的点. ...

  4. 2016 China Final E - Bet

    /************************************************************************* > File Name: E.cpp > ...

  5. 2016 China Final H - Great Cells

    /************************************************************************* > File Name: H.cpp > ...

  6. 2016 China Collegiate Programming Contest Final

    2016 China Collegiate Programming Contest Final Table of Contents 2016 China Collegiate Programming ...

  7. ACM ICPC China final G Pandaria

    目录 ACM ICPC China final G Pandaria ACM ICPC China final G Pandaria 题意:给一张\(n\)个点\(m\)条边的无向图,\(c[i]\) ...

  8. Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution

    Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution 题目链接:https://atcoder.jp/contests/cf16- ...

  9. 2017Summmer_上海金马五校 F题,G题,I题,K题,J题

    以下题目均自己搜 F题  A序列 一开始真的没懂题目什么意思,还以为是要连续的子串,结果发现时序列,简直智障,知道题意之后,好久没搞LIS,有点忘了,复习一波以后,直接双向LIS,处理处两个数组L和R ...

随机推荐

  1. 解压版Tomcat配置

    解压版Tomcat配置(本例Tomcat6):   一 配置Tomcat 1 下载Tomcat Zip压缩包,解压.      如果增加tomcat的用户名和密码,则修改/conf/tomcat-us ...

  2. "\r\n","\r","\n"

    参考: http://blog.csdn.net/xiaofei2010/article/details/8458605 '\r' : 回车符 '\n' : 换行符 以下是c++语言的测试代码, 跟平 ...

  3. C++ 队列的实现

    /************************************************************************/ /* 实现一个通用同步队列 使用链表实现队列 (先 ...

  4. test spring in category

    test tile package com.journaldev.spring.controller; import java.text.DateFormat; import java.util.Da ...

  5. HDNOIP201405杨辉三角

    2016.1.27 试题描述 杨辉三角是形如如下的数字三角形: 1 1    1 1   2    1 …… 现在想求出杨辉三角第N行的N个数中,有多少个数能被给定的质数p整除. 输入 一行两个空格隔 ...

  6. C# Socket简单例子(服务器与客户端通信)

    这个例子只是简单实现了如何使用 Socket 类实现面向连接的通信. 注意:此例子的目的只是为了说明用套接字写程序的大概思路,而不是实际项目中的使用程序.在这个例子中,实际上还有很多问题没有解决,如消 ...

  7. 登录式与非登录式&交互式与非交互式shell及其环境初始化过程

    交互式shell和非交互式shell(interactive shell and non-interactive shell) 交互式模式就是在终端上执行,shell等待你的输入,并且立即执行你提交的 ...

  8. js prepend() 和append()区别

    prepend() 方法在被选元素的开头(仍位于内部)插入指定内容.prepend() 语法:$(selector).prepend(content) 或 $(selector).prepend(fu ...

  9. Sql获取数据集中各类型中的最大值(最新值)

    select * from  (    SELECT t.*,ROW_NUMBER() over (partition by t.pid order by t.op_time desc) num    ...

  10. iOS富文本

    背景:前些天突然想做一个笔记本功能,一开始,觉得挺简单的呀,一个UITextView,网络缓存也不干了,直接本地NSUserDefault存储,然后完事了,美工,弄几张好看的图片,加几个动画,也就这样 ...