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

  先看一下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. 数据库schema设计与优化

    原文地址 1. 前言 对于数据库而言,在日常开发中我们主要的关注点有两块,一个是schema的结构设计,另一个就是索引的优化,这两块是影响我们最终系统结构和性能的关键部分,自然也是我们花费精力最多的部 ...

  2. Bash Shell 获取进程 PID

    转载地址:http://weyo.me/pages/techs/linux-get-pid/ 导读 Linux 的交互式 Shell 与 Shell 脚本存在一定的差异,主要是由于后者存在一个独立的运 ...

  3. vuejs,router

    接下来我们要做的是vue的路由处理,首先当然是安装: npm install vue-router 接下打开我们的main.js,引入我们vue-router,然后在告诉vue我们要使用我们的vue- ...

  4. C++ const用法总结

    1 const分顶层const,底层const2 顶层const可以表示任意类型的对象是常量.eg:     const int i:i是一个整型常量    int *const ccc:ccc是指向 ...

  5. 简单排序,C# 直接使用 List。

    List<string> list = new List<string>(); list.Add("sdfs"); list.Add("ef&qu ...

  6. ./upload/source/class/class_core.php

    定义了core这个类 error_reporting(E_ALL); error_reporting() 设置 PHP 的报错级别并返回当前级别.可以参考手册. define('IN_DISCUZ', ...

  7. (转)设计模式_Singleton单例模式

    静态初始化 public sealed class Singleton { private static readonly Singleton instance = new Singleton(); ...

  8. 【转载】快速收索并更新sid 方法

    利用Google的搜索功能,可以获得不少SAS各个版本的SID号,试过之后你会异常惊喜.1.打开谷歌: http://google.com.hk2.输入或复制这个段文字:"SID_heade ...

  9. 从头开始学算法--NUM operation in MIX

    从前往后,按照课本顺序刚刚看到MIX这部分.NUM是一个转换操作符,可以把字符编码转换为数字.它把registerA & registerX的值转换为数字并赋值给registerA.转换过程是 ...

  10. 时间序列分析之ARIMA模型预测__R篇

    http://www.cnblogs.com/bicoffee/p/3838049.html