字符串作業(四)

「BJOI2020」封印

給出只包含小寫字母aa,bb的兩個字符串ss,ttqq次詢問,每次詢問s[l...r]s[l...r]tt的最長公共子串長度。

建出tt的後綴自動機後在自動機上跑ss串得到對於每個ss的前綴在tt中匹配的最長長度pip_i,然後對於s[l...r]s[l...r],二分答案midmid,檢查是否有maxi=l+mid1rpimid\max_{i=l+mid-1}^rp_i \geq mid即可。

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 400005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define lim 18
using namespace std;

char s[maxn],t[maxn];
int q,ls,lt;
int last , fa[maxn] , len[maxn] , tr[maxn][2] , tot;
int st[lim][maxn] , lg[maxn];
void ins(int c){
	int u = ++tot , p = last , q;
	len[last = u] = len[p] + 1;
	for(;p != -1 && !tr[p][c];p=fa[p]) tr[p][c] = u;
	if(p == -1) fa[u] = 0;
	else if(len[q = tr[p][c]] == len[p] + 1) fa[u] = q;
	else{
		int v = ++tot;
		memcpy(tr[v],tr[q],sizeof tr[q]),fa[v] = fa[q] , len[v] = len[p] + 1;
		for(;p != -1 && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
		fa[q] = fa[u] = v;
	}
}

int qry(int u,int v){
	int t = lg[v-u+1];
	return max(st[t][u] , st[t][v-(1<<t)+1]);
}

int main(){
	scanf("%s%s%d",s,t,&q);
	fa[0] = -1;
	ls = strlen(s) , lt = strlen(t);
	rep(i,0,lt-1) ins(t[i] - 'a');
	int u=0,L=0;
	rep(i,0,ls-1){
		int c = s[i] - 'a';
 		for(;u!=-1 && tr[u][c]==0;u=fa[u]);
 		if(u == -1) u = 0 , L = 0;
 		else L = min(L+1 , len[u] + 1) ,u = tr[u][c];
 		st[0][i] = L;
	}
	rep(i,2,ls) lg[i] = lg[i >> 1] + 1;
	rep(j,1,lim-1) rep(i,0,ls-(1<<j))
		st[j][i] = max(st[j-1][i] , st[j-1][i+(1<<j-1)]);
	for(;q--;){
		int l,r;scanf("%d%d",&l,&r);l--,r--;
		int L = 0 , R = min(r-l+1,lt) , mid;
		for(;L<R;){
			mid = L+R+1 >> 1;
			if(qry(l+mid-1,r) < mid) R = mid - 1;
			else L = mid;
		}
		printf("%d\n",L);
	}
}

LOJ #6537. 毒瘤題加強版再加強版

三倍經驗
具體來說就是利用異或的性質,所有數異或起來等於出現奇數次的數的異或和。
如果我們要得到單獨一個數字,就hashhash一下,具體來說我們只統計modp=i\bmod p = i的數的異或和。
那麼多用幾個pp,統計出來出現次數多的數就很有可能是單獨一個數字的異或和,反之多個數字的異或和很難在多個不同的pp下都一起滿足條件。

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define vi vector<int>
using namespace std;

int chk(int x){
	for(int i=2;i*i<=x;i++) if(x % i == 0) return 0;
	return 1;
}

int n,K;
vi mod;
int a[20][20100];
map<int,int>mAp;

int main(){
	scanf("%d%d",&n,&K);	
	for(int i=10000;mod.size() < 20;i++) if(chk(i) && rand() % 2)
		mod.push_back(i);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);
		for(int j=0;j<mod.size();j++)
			a[j][x % mod[j]] ^= x;
	}
	for(int j=0;j<mod.size();j++) for(int i=0;i<mod[j];i++)
		if(a[j][i]) mAp[a[j][i]]++;
	vector<int>ans;
	for(auto v:mAp) if(v.second > 2) ans.push_back(v.first);
	sort(ans.begin(),ans.end());
	for(int i=0;i<ans.size();i++) printf("%d\n",ans[i]);
}

CF963D Frequency of String

給出SSQQ次詢問SS中最短的子串tt的長度使得字符串mmtt中出現了kk次。
所有詢問m互不相同

可以想到求出後綴自動機的rightright集合SSO(S)O(|S|)回答一次詢問。
然後就這樣寫,然後就可以過。
因爲所有詢問mm互不相同,又因爲對於所有長度爲kk的串他們的rightright集合大小之和爲O(n)O(n)
串不相同代表着只有O(n)O(\sqrt n)種不同的長度,所以所有詢問的複雜度爲O(nn)O(n\sqrt n)
事實上因爲我們只需要mmrightright集合,所以可以不用寫後綴樹上啓發式合併,直接離線求出所有mmACAC自動機,拿ss在自動機上跑,對於 ss的前綴所在的節點我們需要更新所有它的後綴的mmrightright集合,新開一個數組fafa表示沿着failfail鏈跑下一個是mm中的一個的節點,因爲rightright集合大小是O(nn)O(n \sqrt n),每次暴力爬fafa也是O(nn)O(n \sqrt n)的。

來了來了,對於這種屑題,肯定是要寫bitsetbitset的啦。
AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
using namespace std;

char s[maxn],m[maxn];
int n,q,K;
bitset<maxn>C[26];

int main(){
	scanf("%s",s);
	n = strlen(s);
	rep(i,0,n-1) C[s[i] - 'a'][i] = 1;
	scanf("%d",&q);
	for(;q--;){
		scanf("%d%s",&K,m);
		int L = strlen(m);
		static bitset<maxn>ans;
		ans.set();
		rep(i,0,L-1) ans &= C[m[i] - 'a'] >> i;
		vector<int>a;
		for(int i=ans._Find_first();i!=ans.size();i=ans._Find_next(i))
			a.push_back(i);
		if(a.size() < K) puts("-1");
		else{
			int ret = 0x3f3f3f3f;
			rep(i,K-1,a.size()-1) ret = min(ret , a[i] - a[i-K+1] + L); 
			printf("%d\n",ret);
		}
	}
}

CF356E Xenia and String Problem

在這裏插入圖片描述

分類討論屑題。
graygray串的長度只有可能是2k12^k-1,所以可以計算出所有graygray串。
改字符,
一.變爲graygray的情況:
1.改中間字符原來在外面出現過現在沒出現過,暴力哈希判斷即可。
2.改兩邊字符,原來差一個字符匹配現在不差了,對於所有graygray串求出往左/右差一個字符的位置貢獻上去。
二.變成不是graygray的情況:
1.改中間字符原來在外面沒出現過現在出現過,暴力哈希判斷即可。
2.改兩邊字符,原來就是graygray串,只要改了兩邊就一定不是,對於所有graygray串寫區間加即可。

綜上,每次就是先減去改字符是中間字符的貢獻,再減去(二.2),然後枚舉改成那個字符,加上(一.2),加上中間字符的貢獻。

真的屑
AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
#define lim 17
#define LL long long
#define mod 998244353
#define S 131
char s[maxn];
int f[lim][maxn],n;
int sm[26][maxn];
LL hs[maxn],pw[maxn],p12[maxn][26],p22[maxn],Sm;

LL calc(int u,int v){ return ((hs[v] - hs[u-1] * pw[v-u+1]) % mod + mod) % mod; }
int LCP(int u,int v){
	int L = 0 , R = min(n-u+1,n-v+1) , mid;
	while(L<R){
		mid = (L+R+1) >> 1;
		if(calc(u,u+mid-1) == calc(v,v+mid-1))
			L = mid;
		else 
			R = mid - 1;
	}
	return L;
}

int main(){
	
	scanf("%s",s+1);
	n = strlen(s+1);
	pw[0] = 1;
	rep(i,1,n){
		f[0][i] = 1;
		Sm++;
		hs[i] = (hs[i-1] * S + s[i]) % mod;
		pw[i] = pw[i-1] * S % mod;
		rep(j,0,25) 
			sm[j][i] = sm[j][i-1] + (s[i] - 'a' == j);
	}
	rep(j,1,lim-1) rep(i,1,n-(1<<j+1)+2){
		if(f[j-1][i] && f[j-1][i+(1<<j)] && sm[s[i+(1<<j)-1] - 'a'][i+(1<<j+1)-2] - sm[s[i+(1<<j)-1] - 'a'][i-1] == 1
			&& calc(i,i+(1<<j)-2) == calc(i+(1<<j),i+(1<<j+1)-2)) 
			f[j][i] = 1 , Sm += ((1<<j+1)-1ll) * ((1<<j+1)-1ll) , p22[i] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll) , 
			p22[i+(1<<j)-1] -= ((1<<j+1)-1ll) * ((1<<j+1)-1ll) , p22[i+(1<<j)] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll),
			p22[i+(1<<j+1)-1] -= ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
		else if(f[j-1][i]){
			int t = min(LCP(i,i+(1<<j)) , (1<<j)-1);
			if(calc(i,i+t-1) == calc(i+(1<<j),i+(1<<j)+t-1)
				&& calc(i+t+1,i+(1<<j)-2) == calc(i+(1<<j)+t+1,i+(1<<j+1)-2)
				&& sm[s[i+(1<<j)-1] - 'a'][i+(1<<j)-1] - sm[s[i+(1<<j)-1] - 'a'][i-1] == 1)
				p12[i+(1<<j)+t][s[i+t]-'a'] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
				
			if(f[j-1][i+(1<<j)] && calc(i,i+t-1) == calc(i+(1<<j),i+(1<<j)+t-1)
				&& calc(i+t+1,i+(1<<j)-2) == calc(i+(1<<j)+t+1,i+(1<<j+1)-2)
				&& sm[s[i+(1<<j)-1] - 'a'][i+(1<<j+1)-2] - sm[s[i+(1<<j)-1] - 'a'][i+(1<<j)-2] == 1)
				p12[i+t][s[i+t+(1<<j)]-'a'] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
		}
		else if(f[j-1][i+(1<<j)]){
			int t = min(LCP(i,i+(1<<j)) , (1<<j)-1);
			if(calc(i,i+t-1) == calc(i+(1<<j),i+(1<<j)+t-1)
				&& calc(i+t+1,i+(1<<j)-2) == calc(i+(1<<j)+t+1,i+(1<<j+1)-2)
				&& sm[s[i+(1<<j)-1] - 'a'][i+(1<<j+1)-2] - sm[s[i+(1<<j)-1] - 'a'][i+(1<<j)-2] == 1)
				p12[i+t][s[i+t+(1<<j)]-'a'] += ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
		}
	}
	rep(i,1,n) p22[i] += p22[i-1];
	LL ans = Sm;
	rep(i,1,n){
		LL t = Sm;
		rep(j,1,lim-1) if(i-(1<<j)+1 > 0)
			if(f[j][i-(1<<j)+1])
				t -= ((1<<j+1)-1ll) * ((1<<j+1)-1ll);
		t -= p22[i];
		rep(j,0,25) if(j != s[i] - 'a'){
			LL ret = t + p12[i][j];
			
			rep(p,1,lim-1) if(i-(1<<p)+1 > 0){
				if(f[p-1][i-(1<<p)+1] && f[p-1][i+1] && calc(i-(1<<p)+1,i-1) == calc(i+1,i+(1<<p)-1) && 
					sm[j][i-1] - sm[j][i-(1<<p)] == 0)
						ret +=((1<<p+1)-1ll) * ((1<<p+1)-1ll);
			}
			ans = max(ans , ret);
		}
	}
	printf("%lld\n",ans);
}

LOJ #517. 「LibreOJ β Round #2」計算幾何瞎暴力

在這裏插入圖片描述
那就看代碼吧。

#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define inf 0x3f3f3f3f
using namespace std;

int XOR,qXOR,n,tot,ch[maxn*30][2],last,a[maxn];
struct node{
	int a[30],sz;
	void ins(int u){ ++sz;rep(i,0,29) if(u>>i&1) a[i]++; }
	LL sum(int x=inf){
		LL r = 0;
		rep(i,0,29) r += (1ll << i) * min(x , XOR>>i&1 ? sz - a[i] : a[i]);
		return r;
	}
}s[maxn],tr[maxn*30];

void ins(int x){
	for(int u=1,p=29;p>=0;tr[u=ch[u][x>>p&1]].ins(x),p--)
		if(!ch[u][x>>p&1]) ch[u][x>>p&1]=++tot;
}
LL sum(int x){
	if(x > last) return s[x].sum();
	int u=1;LL r = 0;
	per(i,29,0){
		int c=(qXOR>>i&1);
		if(x <= tr[ch[u][c]].sz) u =ch[u][c];
		else r += tr[ch[u][c]].sum(),x-=tr[ch[u][c]].sz,u=ch[u][!c];
	}
	return r + tr[u].sum(x);
}

int main(){
	scanf("%d",&n);int x;
	rep(i,1,n) scanf("%d",&x),a[i]=x,s[i] = s[i-1] , s[i].ins(x);
	int Q;scanf("%d",&Q);
	tot = 1;
	for(int op,l,r;Q--;){
		scanf("%d",&op);
		if(op == 1) scanf("%d",&x),x^=XOR,s[n+1]=s[n],s[++n].ins(x),a[n]=x;
		if(op == 2) scanf("%d%d",&l,&r),printf("%lld\n",sum(r)-sum(l-1));
		if(op == 3) scanf("%d",&x),XOR ^= x;
		if(op == 4) for(qXOR = XOR;last < n;) ins(a[++last]);
	}
}

LOJ #2168. 「POI2011 R3 Day1」週期性 Periodicity

社論
設字符串ss的最短週期爲 LLLL的計算可以簡單通過kmpkmp得到。
那麼對LL分類討論,
L=1L=1則應該對於一個全爲00的字符串。
L=sL=|s|則應該對應一個前L1L-1位全爲00,最後一位爲11的字符串。
2Ls2L \leq |s|,則將字符串分解爲tttt...ttttt...t'的形式,遞歸求tttt'的答案,然後根據最短週期倒推出ss所對應的字符串,之所以可以這樣做,是因爲可以通過有兩個週期p,qp,q並且p+qsp+q \leq |s|那麼他們的gcd\gcd也是週期來證明,最短週期是ll的情況下長度大於l+(smodl)l+(|s|\bmod l)的所有周期其實都是ll的倍數。
因爲我們遞歸求出的答案是一定會滿足所有l+(smodl)\leq l+(|s|\bmod l)的週期,所以tttt..ttttt..t'是可以滿足題意的。
2L>s2L\gt |s|,將字符串分解爲tattat的形式,其中t=smodL|t| = |s| \bmod L,遞歸求tt的答案,發現aa要麼是全爲00,要麼是隻有最後一個爲11,簡單kmpkmp判斷一下即可。
在這裏插入圖片描述

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char s[maxn];
int n,nxt[maxn];

void solve(char *s,int n){
	for(int j=-1,k=0;k<n;)
		if(j == -1 || s[j] == s[k]) nxt[++k] = ++j;
		else j = nxt[j];
	if(nxt[n] == n-1){
		rep(i,0,n-1)s[i] = '0';
		return;
	}
	if(nxt[n] == 0){
		rep(i,0,n-2) s[i] = '0';
		s[n-1] = '1';
		return;
	}
	int L = n - nxt[n];
	if(2 * L <= n){
		solve(s+(n/L-1)*L,n%L+L);
		per(i,n-L-1,0) s[i] = s[i+L];
	}
	else{
		int l = n % L;
		solve(s,l);
		rep(i,1,l) s[n-i] = s[l-i];
		rep(i,l,n-l-1) s[i] = '0';
		 
		for(int j=-1,k=0;k<=n;)
			if(j == -1 || s[j] == s[k]) nxt[++k] = ++j;
			else j = nxt[j];
		if(n - nxt[n] != L) s[n-l-1] = '1';
	}
}

int main(){
	int T;
	scanf("%d",&T);
	nxt[0] = -1;
	for(;T--;){
		scanf("%s",s+1);
		n = strlen(s+1);
		solve(s+1,n);
		puts(s+1);
	}
}

#2278. 「HAOI2017」字符串

如果從前往後第一次失配和從後往前第一次失配的位置之差K1\leq K-1則匹配,求pip_iss中出現次數,那麼我們建出pip_iACAC自動機,對於pip_i的前xx個字符,在ACAC自動機上有個位置aa,對於pi[x+K+1....pi]p_i[x+K+1....|p_i|],我們求出pip_i的反串的ACAC自動機,那麼這個pi[x+K+1....pi]p_i[x+K+1....|p_i|]反過來進入自動機也有一個位置bb
對於ss的前yy個字符在正串自動機上有個位置cc,後syK|s|-y-K個字符在反串自動機上也有個位置dd
sspip_i同時刪去中間的KK個字符後pip_iss中出現,可以看做在ACAC自動機的後綴樹上ccaa的子樹中,ddbb的子樹中。
但是可能會有多種刪去連續KK個字符的方案使得他們相等,會算重,我們發現這些刪去的KK個字符的所有方案是連續的(比如刪去區間是[x,x+K+1],[x+1,x+K+2],[x+2,x+K+3][x,x+K+1],[x+1,x+K+2],[x+2,x+K+3]),所以相鄰兩個方案([x,x+K+1],[x+1,x+K+2][x,x+K+1],[x+1,x+K+2])對應了一個刪去K1K-1個字符的方案(x+1,x+K+1x+1,x+K+1),所以直接減去刪去K1K-1個字符的方案即可,但是因爲[0....K1][0....K-1]不能對應相鄰兩個方案,所以不能計算形如這類的貢獻。
那麼就是兩棵樹,求在兩棵樹中點都在當前點對的點的子樹內的數量,這可以看做二維平面上的數點問題,但是還有更簡單的做法,用天天愛跑步的方法樹上差分後即可用dfsdfs解決一棵樹的限制。

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#define maxn 400005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define pb push_back
#define maxc 94
using namespace std;

int K,n,m;
struct node{int id,x,y;node(const int &id=0,const int &x=0,const int &y=0):id(id),x(x),y(y){}};
vector<node>v1[maxn];
vector<int>v2[maxn];
char s[maxn],t[maxn];
int tr[maxn][maxc] , fa[maxn] , tot , pos[maxn] , ps[maxn];
int st[maxn],ed[maxn],tim,ans[maxn];
vector<int>G[maxn];
void dfs(int u){
	st[u] = ++tim;
	for(int v:G[u]) dfs(v);
	ed[u] = tim; 
}
int t1[maxn],t2[maxn];
void upd(int u,int v,int *tr){ for(;u<=tot+1;u+=u&-u) tr[u] += v; }
int qry(int u,int *tr){ int r=0;for(;u>0;u-=u&-u) r += tr[u]; return r; }
void dfs2(int u){
	for(node t:v1[u]) ans[t.id] -= qry(ed[t.x],t1) - qry(st[t.x]-1,t1) - qry(ed[t.y],t2) + qry(st[t.y]-1,t2);
	for(int t:v2[u]) upd(st[pos[t+K+1]],1,t1),upd(st[pos[t+K]],1,t2);
	for(int v:G[u]) dfs2(v);
	for(node t:v1[u]) ans[t.id] += qry(ed[t.x],t1) - qry(st[t.x]-1,t1) - qry(ed[t.y],t2) + qry(st[t.y]-1,t2);
}
int main(){
	scanf("%d%s%d",&K,s+1,&n);
	m = strlen(s+1);
	rep(i,1,n){
		scanf("%s",t+1);
		int L = strlen(t+1);
		if(L <= K){
			ans[i] = m-L+1;
			continue;
		}
		int u = 0;
		rep(i,1,L){
			int v = t[i]-33;
			if(!tr[u][v]) tr[u][v] = ++tot;
			u = tr[u][v];
		}
		ps[L+1] = u = 0;
		per(i,L,1){
			int v = t[i] - 33;
			if(!tr[u][v]) tr[u][v] = ++tot;
			u = tr[u][v];
			ps[i] = u;
		}
		for(int j=0,u=0;j+K<=L;u=tr[u][t[j+1]-33],j++)	
			v1[u].pb(node(i,ps[j+K+1],j?ps[j+K]:maxn-1));
	}
	static int q[maxn],L=0,R=0;
	rep(i,0,93) if(tr[0][i]) q[R++] = tr[0][i];
	for(int u;L<R;){
		u = q[L++];
		rep(i,0,93) if(tr[u][i]) fa[tr[u][i]] = tr[fa[u]][i] , q[R++] = tr[u][i];
			else tr[u][i] = tr[fa[u]][i];
	}
	rep(i,1,tot) G[fa[i]].pb(i);
	dfs(0);
	for(int i=m,u=0;i>=1;i--) u=tr[u][s[i]-33] , pos[i] = u;
	for(int i=0,u=0;i+K<=m;i++) v2[u].pb(i),u=tr[u][s[i+1]-33];
	dfs2(0);
	rep(i,1,n) printf("%d\n",ans[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章