【BZOJ5417】你的名字(NOI2018)-字尾自動機+主席樹
測試地址:你的名字
做法:本題需要用到字尾自動機+主席樹。
首先考慮的情況。考慮的每個字首的貢獻,我們需要找到它最短的沒在中出現過,而且沒在的前面部分出現過的字尾,這樣包含它的所有後綴就都是合法的貢獻了。顯然這個字尾的長度等同於,在中出現過或者在的前面部分出現過的最長的字尾的長度加。因此我們分開考慮兩種情況。對於在的前面部分出現過的最長字尾,可以通過對建字尾自動機,利用字尾樹的性質,決定每個點所代表的串在哪個字首中第一次出現,DFS一遍統計貢獻即可。而對於在中出現過的最長字尾,顯然就是用在的字尾自動機中匹配,匹配的長度就是最長字尾長度。這樣我們就能解決68分的部分了。
再考慮拓展到
首先如果目前的匹配點完全沒有字元的指標,顯然就不能繼續走,要根據字尾連結跳回去再進行試探。如果有字元的指標,也不一定就能走,因為那個字串雖然出現在了中,但不一定出現在指定的區間中。這時候要判斷在某個區間中存不存在當前字串。根據字尾自動機的性質,每個點有一個集合,即該點所代表的字串可能出現的右端點的集合,而這個集合顯然就是這個點在後綴樹上的子樹中所包含的所有字首節點。我們要判斷的是,集合中存不存在一個,使得,而且,即。如果我們對字尾樹維護一個DFS序,那麼這就變成了在區間中詢問存不存在一個權值在某區間內的數,這就是主席樹的經典應用了,所以我們使用主席樹來判斷能不能往下走。
還有一點要注意,如果當前不能走,並不是直接通過後綴連結回退到上面的點,因為當前點還是可能有很多長度不一的字尾的,那麼現在的問題就是在長度集合中,最大的使得權值區間在上述演算法的詢問中合法的,這個就是我們所回退到的串的長度,這時候再往下走就行了。這個東西可以在主席樹上分治得到(需要做點處理,詳見程式碼)。當然,如果找不到這個點,就沿著字尾連結退回上一個點。
由於回退和前進的次數都是線性的,所以以上演算法的時間複雜度是,可以通過此題。
總的來說,這道題目考察了字尾自動機本身作為匹配工具的性質,又考察了作為輔助的字尾連結,也就是字尾樹的性質,還考察了將高階資料結構——主席樹維護DFS序的方法在後綴樹上靈活運用的能力,思路自然,碼量適中,給出題人點贊(我是認真的)。
(但據說已經是套路題了……)
以下是本人程式碼:
#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,m,tot,last,totp,first[2000010],tote;
int ch[2000010][26],pre[2000010],len[2000010],vis[2000010]={0};
int tim=0,in[1000010],out[1000010],mx[500010];
int totseg=0,rt[500010]={0},seg[10000010]={0},segch[10000010][2]={0};
int qlen,ql,qr;
char s[500010];
struct edge
{
int v,next;
}e[2000010];
void extend(int rt,int c)
{
int p,q,np,nq;
p=last;
np=++tot;
len[np]=len[p]+1;
for(int i=0;i<26;i++)
ch[np][i]=0;
while(p&&!ch[p][c]) ch[p][c]=np,p=pre[p];
if (p)
{
q=ch[p][c];
if (len[p]+1==len[q]) pre[np]=q;
else
{
nq=++tot;
for(int i=0;i<26;i++)
ch[nq][i]=ch[q][i];
len[nq]=len[p]+1;
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
while(p&&ch[p][c]==q) ch[p][c]=nq,p=pre[p];
}
}
else pre[np]=rt;
last=np;
}
void insert(int a,int b)
{
e[++tote].v=b;
e[tote].next=first[a];
first[a]=tote;
}
void build(int start,int n)
{
tot=last=start;
pre[start]=len[start]=0;
len[0]=-1;
for(int i=0;i<26;i++)
ch[start][i]=0;
for(int i=1;i<=n;i++)
{
extend(start,s[i]-'a');
vis[last]=i;
}
tote=0;
for(int i=start;i<=tot;i++)
first[i]=0;
for(int i=start+1;i<=tot;i++)
insert(pre[i],i);
}
void pushup(int no)
{
seg[no]=seg[segch[no][0]]+seg[segch[no][1]];
}
void insert(int &v,int last,int l,int r,int x)
{
v=++totseg;
seg[v]=seg[last];
segch[v][0]=segch[last][0];
segch[v][1]=segch[last][1];
if (l==r) {seg[v]++;return;}
int mid=(l+r)>>1;
if (x<=mid) insert(segch[v][0],segch[last][0],l,mid,x);
else insert(segch[v][1],segch[last][1],mid+1,r,x);
pushup(v);
}
void dfs1(int v)
{
in[v]=tim+1;
if (vis[v])
{
tim++;
insert(rt[tim],rt[tim-1],1,n,vis[v]);
}
for(int i=first[v];i;i=e[i].next)
dfs1(e[i].v);
out[v]=tim;
}
void dfs2(int v)
{
if (!vis[v]) vis[v]=inf;
for(int i=first[v];i;i=e[i].next)
{
dfs2(e[i].v);
vis[v]=min(vis[v],vis[e[i].v]);
}
for(int i=first[v];i;i=e[i].next)
if (vis[v]!=vis[e[i].v])
mx[vis[e[i].v]]=len[v];
if (v==totp+1) mx[vis[v]]=0;
}
int querysum(int vr,int vl,int l,int r,int s,int t)
{
if (l>=s&&r<=t) return seg[vr]-seg[vl];
int mid=(l+r)>>1,ans=0;
if (s<=mid) ans+=querysum(segch[vr][0],segch[vl][0],l,mid,s,t);
if (t>mid) ans+=querysum(segch[vr][1],segch[vl][1],mid+1,r,s,t);
return ans;
}
int querylast(int vr,int vl,int l,int r,int s,int t)
{
if (seg[vr]-seg[vl]==0) return 0;
if (l==r) return l;
int mid=(l+r)>>1,ans=0;
if (t>mid) ans=querylast(segch[vr][1],segch[vl][1],mid+1,r,s,t);
if (ans) return ans;
if (s<=mid) ans=querylast(segch[vr][0],segch[vl][0],l,mid,s,t);
return ans;
}
void solve()
{
int now=1,d=0,l,r;
long long ans=0;
scanf("%d%d",&l,&r);
for(int i=1;i<=qlen;i++)
{
int c=s[i]-'a';
while(now)
{
if (!ch[now][c]) {now=pre[now],d=len[now];continue;}
int s=len[pre[now]]+1,t=min(d,r-l);
if (s>t||l+s>r) {now=pre[now],d=len[now];continue;}
int rta=rt[out[ch[now][c]]],rtb=rt[in[ch[now][c]]-1];
if (l+t+1>r||querysum(rta,rtb,1,n,l+t+1,r)==0)
{
int x=querylast(rta,rtb,1,n,l+s,l+t);
if (!x) {now=pre[now],d=len[now];continue;}
now=ch[now][c],d=x-l+1;break;
}
else {now=ch[now][c],d=t+1;break;}
}
if (!now) now=1,d=0;
ans+=(long long)(i-max(mx[i],d));
}
printf("%lld\n",ans);
}
int main()
{
scanf("%s",s+1);
s[0]='#';
n=strlen(s)-1;
build(1,n);
totp=tot;
dfs1(1);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s+1);
qlen=strlen(s)-1;
for(int j=totp+1;j<=totp+(qlen<<1)+1;j++)
vis[j]=0;
build(totp+1,qlen);
dfs2(totp+1);
solve();
}
return 0;
}
相關推薦
【BZOJ5417】你的名字(NOI2018)-字尾自動機+主席樹
測試地址:你的名字 做法:本題需要用到字尾自動機+主席樹。 首先考慮l=1,r=|S|l=1,r=|S|的情況。考慮TT的每個字首的貢獻,我們需要找到它最短的沒在SS中出現過,而且沒在TT的前面部分出現過的字尾,這樣包含它的所有後綴就都是合法的貢獻了。顯然這
[BZOJ5417/UOJ#395/NOI2018]你的名字(字尾自動機+主席樹)
Address Solution… 68pts:l=1,r=∣S∣l=1,r=|S|l=1,r=∣S∣ 建議先做:[BZOJ4566][Haoi2016]找相同字元 詢問前先建出 SSS 的字尾自動機,然後求出 TTT 的每個字首 T[1...i]T[1...
【模板】負環(spfa)
sizeof 貪心 com image 一行 clas 存在 cst -m 洛谷——P3385 【模板】負環 題目描述 暴力枚舉/SPFA/Bellman-ford/奇怪的貪心/超神搜索 輸入輸出格式 輸入格式: 第一行一個正整數T表
【模板】矩陣加速(數列)
cst opera name 結果 ++ 取余 int 數列 names 題目描述 a[1]=a[2]=a[3]=1 a[x]=a[x-3]+a[x-1] (x>3) 求a數列的第n項對1000000007(10^9+7)取余的值。 輸入輸出格式 輸入格式: 第一行一
【轉】SVM入門(六)線性分類器的求解——問題的轉化,直觀角度
content cli 樣本 image ges 五個 是你 角度 spa SVM入門(六)線性分類器的求解——問題的轉化,直觀角度 讓我再一次比較完整的重復一下我們要解決的問題:我們有屬於兩個類別的樣本點(並不限定這些點在二維空間中)若幹,如圖, 圓形的樣本點定為正樣
【leetcode】Word Break(python)
條件 text for -m 是我 tex eas sso false 思路是這種。我們從第一個字符開始向後依次找,直到找到一個斷句的地方,使得當前獲得的子串在dict中,若找到最後都沒找到。那麽就是False了。 在找到第一個後,接下來找下一個斷句處,當然是從第
【轉】JMeter學習(四)參數化
一個 ngx adr conf 英文逗號 .net 註意 itl ron JMeter也有像LR中的參數化,本篇就來介紹下JMeter的參數化如何去實現。 參數化:錄制腳本中有登錄操作,需要輸入用戶名和密碼,假如系統不允許相同的用戶名和密碼同時登錄,或者想更好的模擬多個
【轉】JMeter學習(三)元件的作用域與執行順序
ces ner 處理器 規則 fig 子節點 控制器 conf 節點 1.元件的作用域 JMeter中共有8類可被執行的元件(測試計劃與線程組不屬於元件),這些元件中,取樣器是典型的不與其它元件發生交互作用的元件,邏輯控制器只對其子節點的取樣器有效,而其它元件(config
【轉】JMeter學習(二)錄制腳本
使用 get 運行 喜歡 錄制完成 帶來 免費 sdn title ---------------------------------------------------------------------------------------------------- 環境
P1939 【模板】矩陣加速(數列)
include algo pid str ostream 格式 矩陣加速 continue pri 鏈接: P1939 【模板】矩陣加速(數列) 題目描述 a[1]=a[2]=a[3]=1 a[x]=a[x-3]+a[x-1] (x>3) 求a數列的第n項對
【Fortinet】飛塔(FortiGate)防火墻低端產品命令行下配置RIP
rip fortinet forgate 飛塔防火墻 命令行配置rip 前言:FortiGate中端、高端產品支持web頁面配置RIP/OSPF/BGP,低端(桌面級)產品不支持,只支持CLI配置------雖然官網有手冊(英文版),但沒有實際案例,並給出建立連接的結果來的舒服~~這就是此
【貪心】紀念品分組(P1094)
scanf name print 所有 namespace 整數 數據 %d 輸出格式 題目描述 元旦快到了,校學生會讓樂樂負責新年晚會的紀念品發放工作。為使得參加晚會的同學所獲得 的紀念品價值相對均衡,他要把購來的紀念品根據價格進行分組,但每組最多只能包括兩件紀念品,
【4】 簡單繪圖(二)
dispose alt draw bsp rom 形狀 .html yellow tex 在上一篇裏已經向大家介紹了如何使用GDI+繪制簡單的圖像,這一篇繼續向大家介紹其它一些繪圖知識. 1.首先我們來看下上一篇中我們使用過的Pen. Pen的屬性主要有: Color(顏色
luogu_1939 【模板】矩陣加速(數列)
iostream urn 加速 spa con () truct highlight ems #include <cstdio> #include <iostream> #include <cstring> using namespac
【轉】VBA 入門 (一)
文件的 方法 記錄 glob 數字 數據 list 目錄 處理 VBA語言的基礎認識 由 vietdung90 創建,最後一次修改 2016-10-18 【轉自W3CSCHOOL】 第一節:標識符 一、定義 標識符是一種標識變量、常量、過程、函數、類等語言構
【CODEFORCES】 891B Gluttony(構造)
ray markdown tinc lower blog clu include first right codeforces 891B Gluttony 鏈接:http://codeforces.com/problemset/problem/891/B Descripti
【UVA】201 Squares(模擬)
ref size || for != %d else eof mark 題目 題目 ? ? 分析 記錄一下再預處理一下。 ? ? 代碼 #include <bits/stdc++.h> int main() { int t=0,s,n; wh
【UVa】208 Firetruck(dfs)
-m main truck 開始 clear %d span 需要 from 題目 題目 ? ? 分析 一開始不信lrj的話,沒判聯通,果然T了。 沒必要全部跑一遍判,只需要判斷一下有沒有點與n聯通,鄰接表不太好判,但無向圖可以轉換成去判n與什麽聯通。 關於為什麽要判,還
【LightOJ1336】Sigma Function(數論)
【LightOJ1336】Sigma Function(數論) 題面 Vjudge 求和運算是一種有趣的操作,它來源於古希臘字母σ,現在我們來求一個數字的所有因子之和。例如σ(24)=1+2+3+4+6+8+12+24=60.對於小的數字求和是非常的簡單,但是對於大數字求和就比較困難了。現在給你一個n,你需要
洛谷P3803 【模板】多項式乘法(FFT) 【fft】
n+1 swap 提示 接下來 bug ret const define %d 題目 這是一道FFT模板題 輸入格式 給定一個n次多項式F(x),和一個m次多項式G(x)。 請求出F(x)和G(x)的卷積。 輸出格式 第一行2個正整數n,m。 接下來一行n+1個數字,從低到