题意:

给你N个串对,M个询问串,对每个询问串求是多少串对的子串(在串对的某一个中作为子串),以及每个串对最终是包含了多少询问串

方法众多。。

可谓字符串家族八仙过海各显神通。

复杂度不尽相同,O(nlogn),O(nsqrt(n)),O(玄学)(也就是暴力)

(数据比较水,所以一些暴力就过去了)

做法基本都是离线。

法一:AC自动机+暴力

对询问串建AC自动机,把主串往上跑。

匹配到了一个节点,就暴力跳fail,把沿途的点如果是询问串的结尾,ans++,并打上标记,防止重复计数。

一串1111111,随便卡。

法二:AC自动机+hash

询问串或者主串可能有相同的,,,hash一下,减少理论复杂度。。。

还是随便卡。

法三:AC自动机+fail树虚树

这个是正解O(nlogn)。

但是不会虚树,咕咕咕。

法四:后缀数组+hash

AC自动机比较辣鸡,后缀家族表示不服。

一个后缀的和询问串的lcp是询问串的长度的话,那么这个询问串就是子串。

考虑一个询问会被哪些后缀包含,我们把所有的询问串和子串用分隔符隔开,然后跑SA,HEIGHT

对于每个询问串的开头位置,往左往右二分出所有出现的位置。

这个区间[l,r]就是这个询问串的所有出现位置~!

luogu题解第一篇dalao说,可以不用二分,线性处理出l,r的位置。

然后我写了单调队列,,,,然鹅显然这个区间端点并没有单调性。。。然后WA了半天。。。。

不知是这个dalao口胡错了,还是我太菜了?

怎么统计答案?

由于区间长度期(shu)望(ju)不(tai)大(shui),可以暴力扫一遍这个区间,然后轻松统计答案。

一串111111,应该还是能卡。

法五:后缀数组+莫队

莫队教导我们:干嘛要直接暴力?

处理出询问区间之后,莫队可以轻松统计第一问,

第二问的话:差分。每新加入一个颜色,就把这个颜色的答案加上剩余询问的次数,删除这个颜色的时候,就把剩余次数减掉。这样,处理所有包含这个颜色的询问的时候,这些询问一定贡献到了这个颜色里。

O(nsqrt(n))

法六:后缀数组+树状数组

莫队归根到底,还是暴力啊。。。。

再看一看第一问是什么:统计区间颜色的数量?哦,,[SDOI2009]HH的项链!!

树状数组来也。

第二问呢?反过来,把询问当做树状数组中加入的点值。

到L的时候,bit(L)++,到R的时候,bit(L)--,到i的时候,ans+=query(i)-query(pre[i])

类似扫描线。

本质上还是对于每个颜色第一次被区间包含的时候,把这个区间的贡献加上。为什么这里要把bit(L)--?为了消除区间两端在中间的情况。

那为什么不把bit(R)--?这样前面的相同部分并不能减去

反而加上了-1

我写的就是这个方法:

注意:

1.还是二分吧。。

2.注意我们是在SA数组上操作,pos记录的是原串的所属,所以,在SA上循环的时候,查询这个后缀的所属,用pos[sa[i]]

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=4e5+;
const int C=1e4+;
int x[N],y[N],c[N],sa[N],hei[N],rk[N];
int n,m;
int s[N+];
void SA(int n){
int m=C+;
for(reg i=;i<=n;++i) ++c[x[i]=s[i]];
for(reg i=;i<=m;++i) c[i]+=c[i-];
for(reg i=;i<=n;++i) sa[c[x[i]]--]=i;
for(reg k=;k<=n;k<<=){
int num=;
for(reg i=n-k+;i<=n;++i) y[++num]=i;
for(reg i=;i<=n;++i){
if(sa[i]-k>=) y[++num]=sa[i]-k;
}
for(reg i=;i<=m;++i) c[i]=;
for(reg i=;i<=n;++i) ++c[x[i]];
for(reg i=;i<=m;++i) c[i]+=c[i-];
for(reg i=n;i>=;--i) sa[c[x[y[i]]]--]=y[i],y[i]=;
swap(x,y);
num=;
x[sa[]]=;
for(reg i=;i<=n;++i){
x[sa[i]]=((y[sa[i]]==y[sa[i-]])&&(y[sa[i-]+k]==y[sa[i]+k])?num:++num);
}
if(num==n) break;
m=num;
}
}
void HEI(int n){
for(reg i=;i<=n;++i) rk[sa[i]]=i;
int k=;
for(reg i=;i<=n;++i){
if(k)--k;
if(rk[i]==) continue;
int j=sa[rk[i]-];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;
hei[rk[i]]=k;
}
}
int pos[N],pre[N],las[N],id[N];
int nxt[N];
struct question{
int L,R,id,ans;
bool friend operator <(question a,question b){
return a.L<b.L;
}
}que[N];
struct node{
int L,p,c;
bool friend operator <(node a,node b){
if(a.p!=b.p) return a.p<b.p;
return a.c>b.c;
}
}po[*N];
int cnt;
int chang[N];
int f[N][];
int lg[N];
int tot;
int query(int x,int y){
if(x==y) return tot-x+;
if(x>y) swap(x,y);
++x;
int len=lg[y-x+];
// cout<<" rmq "<<len<<" "<<f[x][len]<<" "<<f[y-(1<<len)+1][len]<<endl;
int ret=min(f[x][len],f[y-(<<len)+][len]);
return ret;
}
void prewrk(int n){
/// cout<<" nn "<<n<<endl;
for(reg i=1;i<=n;++i) {
f[i][0]=hei[i];
lg[i]=(i>>(lg[i-]+))?lg[i-]+:lg[i-];
}
for(reg j=;j<=;++j){
for(reg i=;i+(<<j)-<=n;++i){
f[i][j]=min(f[i][j-],f[i+(<<(j-))][j-]);
//cout<<" i j "<<i<<" "<<j<<" : "<<f[i][j]<<endl;
}
} for(reg i=;i<=n;++i){
if(pos[sa[i]]==&&id[sa[i]]>){
int tmp=i;
int l=,r=i-; que[id[sa[i]]].id=id[sa[i]];
while(l<=r){
int mid=(l+r)>>;
if(query(mid,i)>=chang[id[sa[i]]]) tmp=mid,r=mid-;
else l=mid+;
}
que[id[sa[i]]].L=tmp; l=i+,r=n;
tmp=i;
while(l<=r){
int mid=(l+r)>>;
if(query(i,mid)>=chang[id[sa[i]]]) tmp=mid,l=mid+;
else r=mid-;
}
que[id[sa[i]]].R=tmp;
}
}
}
int ans1[N];
int ans2[N];
struct arraytree{
int f[N];
void add(int x,int c){
for(;x<=tot;x+=x&(-x)) f[x]+=c;
}
int query(int x){
int ret=;
for(;x;x-=x&(-x)) ret+=f[x];
return ret;
}
}t;
int main(){
rd(n);rd(m);
int len,x;
tot=;
for(reg i=;i<=n;++i){
rd(len);
for(reg j=;j<=len;++j){
rd(x);
s[++tot]=x;
id[tot]=;
pos[tot]=i;
}
s[++tot]=C;//warning!!!
pos[tot]=-;//warning!! -1
rd(len);
for(reg j=;j<=len;++j){
rd(x);
s[++tot]=x;
id[tot]=;
pos[tot]=i;
}
s[++tot]=C;
pos[tot]=-;
}
for(reg i=;i<=m;++i){
rd(len);
chang[i]=len;
for(reg j=;j<=len;++j){
rd(x);
s[++tot]=x;
pos[tot]=;
if(j==) id[tot]=i;
}
s[++tot]=C;
pos[tot]=-;
}
SA(tot); HEI(tot); prewrk(tot); // cout<<" after "<<endl;
//
sort(que+,que+m+);
//
// for(reg i=1;i<=tot;++i){
// cout<<s[i]<<" ";
// }cout<<endl<<endl;
////
// for(reg i=1;i<=tot;++i){
// cout<<pos[sa[i]]<<" ";
// }cout<<endl<<endl;
////
// for(reg i=1;i<=tot;++i){
// cout<<id[sa[i]]<<" ";
// }cout<<endl<<endl;
// for(reg i=1;i<=tot;++i){
// cout<<hei[i]<<" ";
// }cout<<endl<<endl; for(reg i=;i<=m;++i){
// cout<<que[i].L<<" "<<que[i].R<<" "<<que[i].id<<endl;
po[++cnt].c=;
po[cnt].L=que[i].L,po[cnt].p=que[i].L; po[++cnt].c=-;
po[cnt].L=que[i].L,po[cnt].p=que[i].R;
}
sort(po+,po+cnt+); for(reg i=;i<=tot;++i){
if(pos[sa[i]]>){
pre[i]=las[pos[sa[i]]];
las[pos[sa[i]]]=i;
}
}
memset(las,,sizeof las);
for(reg i=tot;i>=;--i){
if(pos[sa[i]]>){
if(las[pos[sa[i]]])nxt[i]=las[pos[sa[i]]];
else nxt[i]=tot+;
las[pos[sa[i]]]=i;
}
}
int now=;
for(reg i=;i<=n;++i){
if(las[i]){
//cout<<i<<" add fir "<<las[i]<<endl;
t.add(las[i],);
}
}
for(reg i=;i<=tot;++i){
while(now<=m&&que[now].L==i){
//<<que[now].id<<" : "<<que[now].R<<" "<<t.query(que[now].R)<<" "<<que[now].L<<" "<<t.query(que[now].L-1)<<endl;
que[now].ans=t.query(que[now].R)-t.query(que[now].L-);
ans1[que[now].id]=que[now].ans;
++now;
}
if(nxt[i]&&nxt[i]<=tot) t.add(nxt[i],);
}
memset(t.f,,sizeof t.f);
now=;
for(reg i=;i<=tot;++i){
while(po[now].p==i&&now<=cnt&&po[now].c==){
t.add(po[now].L,po[now].c);
++now;
}
if(pos[sa[i]]>){
ans2[pos[sa[i]]]+=t.query(i)-t.query(pre[i]);
}
while(po[now].p==i&&now<=cnt){
t.add(po[now].L,po[now].c);
++now;
}
}
for(reg i=;i<=m;++i){
printf("%d\n",ans1[i]);
}
for(reg i=;i<=n;++i){
printf("%d ",ans2[i]);
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/12/23 11:39:01
*/

法七:后缀数组+主席树+树状数组

emmm。。。

第一问也可以用主席树做。。。(虽然已经离线了,就完全没有必要了)

法八:后缀自动机+暴力

SAM大吼一声,怎么能少了俺?!?!

对所有的姓、名建广义SAM

可以对每个串的每个位置暴力跳parent树,把每个right集合的位置实际在多少个串上出现,叫做sz,记录下来。

询问的话,匹配一遍,如果中途没有失配,最后的匹配到节点的sz即为答案。

然后在这个点上打上tag++

最后,把所有的姓名串的每个位置再跑一遍,tag的总和就是被点名次数。当然,要在途中留下自己的标记,以防重复统计。

还是暴力。

一串111111应该还是可以卡掉?因为parent树退化成了一条链。

但是值得一提的是,这个算法总算是在线的!

法九:后缀自动机+莫队

思路来自:ywy_c_asm

莫队支持区间,那后缀自动机哪里有区间?

先把每个询问串在后缀自动机上跑一下,(失配直接puts0,然后滚蛋)

最后到了某个节点p,那么根据parent树的意义,p的子树中的所有点代表的位置都包含这个询问串!

于是,我们用莫队来搞dfn序!(具体所属情况,叶子就记录了。)

然后的方法大家就已经很熟悉了。(当然也可以用主席树或者树状数组做。)


upda:2019.3.8:

法十:后缀自动机+线段树合并

在线+O(nlogn)的算法

建出广义SAM,然后线段树合并,

跑询问串时候,查询线段树的sz就是第一问答案。然后打上tag标记

最后再来一次线段树合并。

到每个点时候把tag整个加到线段树上去。

到了root,dfs一遍线段树即可。

[SCOI2012]喵星球上的点名——堪称十种方法做的题的更多相关文章

  1. BZOJ 2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 649  Solved: 305[Submit][Sta ...

  2. BZOJ2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 680  Solved: 314[Submit][Sta ...

  3. BZOJ 2754: [SCOI2012]喵星球上的点名 [后缀数组+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1906  Solved: 839[Submit][St ...

  4. BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1902  Solved: 837[Submit][St ...

  5. P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)

    P2336 [SCOI2012]喵星球上的点名 名字怎么存?显然是后缀自动机辣 询问点到多少个喵喵喵其实就是 查询后缀自动机上parent树的一个子树 于是我们考虑莫队 怎么树上莫队呢 我们用dfs序 ...

  6. 洛咕 P2336 [SCOI2012]喵星球上的点名

    洛咕 P2336 [SCOI2012]喵星球上的点名 先求出SA和height,一个点名串对应的就是一段区间,还有很多个点,就转化成了 有很多个区间,很多个点集,对每个区间计算和多少个点集有交,对每个 ...

  7. 洛谷 P2336 [SCOI2012]喵星球上的点名 解题报告

    P2336 [SCOI2012]喵星球上的点名 题目描述 a180285 幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有 \(N\) 个喵星人,每个喵星人的 ...

  8. 【BZOJ2754】[SCOI2012]喵星球上的点名

    [BZOJ2754][SCOI2012]喵星球上的点名 题面 bzoj 洛谷 题解 这题有各种神仙做法啊,什么暴力\(AC\)自动机.\(SAM\)等等五花八门 我这个蒟蒻在这里提供一种复杂度正确且常 ...

  9. 2754. [SCOI2012]喵星球上的点名【后缀数组】

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...

随机推荐

  1. windows环境下安装scrapy框架报错问题--最快捷有效的解决方案

    windows在执行如下命令,安装scrapy的过程中会报错: pip install scrapy 报错分析: windows环境下,会出现如下错误: 1.提示的错误是编译环境的问题,字面意思看需要 ...

  2. 网站漏洞修复之最新版本UEditor漏洞

    UEditor于近日被曝出高危漏洞,包括目前官方UEditor 1.4.3.3 最新版本,都受到此漏洞的影响,ueditor是百度官方技术团队开发的一套前端编辑器,可以上传图片,写文字,支持自定义的h ...

  3. POJ1236 tarjan

    Network of Schools Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 19613   Accepted: 77 ...

  4. python2.7练习小例子(十七)

        17):题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字.例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制.     程序分析: ...

  5. [【转】ubuntu 16.10 Server 安装及基本部署

    一.Ubuntu Server 16.10 LTS 系统安装 Ubuntu 16.10 分为 桌面版 (desktop)和服务器版(Server).两者对于用户而言,最大的区别在于桌面版有图形操作界面 ...

  6. struts2官方 中文教程 系列十二:控制标签

    介绍 struts2有一些控制语句的标签,本教程中我们将讨论如何使用 if 和iterator 标签.更多的控制标签可以参见 tags reference. 到此我们新建一个struts2 web 项 ...

  7. ip4addr_ntoa和不可重入函数

    在网络中,有一个转换IP地址到ASIIC字符串的函数,该函数的返回值所指向的ASIIC字符串驻留在静态内存中,所以该函数不可重入. 通俗的讲,在多任务系统中,一个任务执行在调用运行这个函数的时候,其他 ...

  8. nginx https ssl 配置

    #设置https 访问server { listen ; server_name www.xxx.com; access_log xxx/xxx/xxx.log combined; index ind ...

  9. GreenMail邮件测试服务器

    GreenMail邮件测试服务器 http://blog.csdn.net/jackiehff/article/details/8741988 这个目前没有需求,所以暂不研究

  10. ASP.NET MVC 使用jquery.form.js 异步上传 在IE下返回值被变为下载的解决办法

    错误记录: <script type="text/javascript"> $(function () { $(document).off("ajaxSend ...