Loading

P4094 字符串

P4094 字符串

简化题意

给定字符串 \(s\), 每次询问给定两个字符串 \([a , b]\)\([c , d]\), 求前串的所有子串和后串的最长公共前缀。
\(n \le 10 ^ 5 , m \le 10 ^ 5\)

题解

感觉其实这道题并不是特别难的,就是代码长,不折不扣的码农题。

刚开始有一个错误的想法,就是主席树维护 \(Rank\) 值查离 \(Rank_c\) 前驱后继两个值。

这个显然就是错的,因为可能匹配出的 \(LCP\) 值越过了 \(b\),导致可能前面的长度要更优。

正解是二分答案,当钦定答案为 \(mid\) 时,显然区间为 \([a , b - mid + 1]\)

先二分 \(Rank_y\) 上下界,使上下界为第一个和最后一个与 \(y \ LCP \ge mid\) 的位置。

主席树上查在这个区间是否有值即可。

复杂度为 \(\mathcal{O}(n \log^2 n)\)

不卡常。

Code

CODE
#include <bits/stdc++.h>
using namespace std ; 
const int N = 1e5 + 100 ; 
char s[N] ; 
int n , M ; 

namespace CharacterString {
	namespace SuffixArray {
		/*This is a algorithm in order to find suffixes and sort for them with the time complexity O(nlogn).
		It supports the operation of getting the longest prefix.*/
		int SA[N] , Rank[N] , m = 127 , p , Lstrk[N] , id[N] , cnt[N] , key1[N] ; 
		int lg[N] , ST[21][N] ; 

		inline void Suffix_Array() {
			memset(cnt , 0 , sizeof(cnt)) ; 
			auto Compare = [](int x , int y , int j) {
				return Lstrk[x] == Lstrk[y] && Lstrk[x + j] == Lstrk[y + j] ; 
			} ; 

			for (int i = 1 ; i <= n ; ++ i) cnt[Rank[i] = s[i]] ++ ; 
			for (int i = 1 ; i <= m ; ++ i) cnt[i] += cnt[i - 1] ; 
			for (int i = n ; i >= 1 ; -- i) SA[cnt[Rank[i]] --] = i ; 

			for (int j = 1 ; ; m = p , j <<= 1) {
				p = 0 ; 

				for (int i = n ; i > n - j ; -- i) id[++ p] = i ; 
				for (int i = 1 ; i <= n ; ++ i) {
					if (SA[i] - j > 0) {
						id[++ p] = SA[i] - j ; 
					}
				}

				memset(cnt , 0 , sizeof(cnt)) ; 
				for (int i = 1 ; i <= n ; ++ i) {
					++ cnt[key1[i] = Rank[id[i]]] ; 
				}
				for (int i = 1 ; i <= m ; ++ i) cnt[i] += cnt[i - 1] ; 
				for (int i = n ; i >= 1 ; -- i) {
					SA[cnt[key1[i]] --] = id[i] ; 
				}
				memcpy(Lstrk , Rank , sizeof(Rank)) ; p = 0 ; 

				for (int i = 1 ; i <= n ; ++ i) {
					Rank[SA[i]] = Compare(SA[i] , SA[i - 1] , j) ? p : ++ p ; 
				}

				if (p == n) break ; 
			} 
		}

		inline int MinST(int l , int r) {
			int k = lg[r - l + 1] ; 
			return min(ST[k][l] , ST[k][r - (1 << k) + 1]) ; 
		}

		inline int LCP(int l , int r) {
			if (l == r) return n - l + 1 ; 
			if (Rank[l] > Rank[r]) swap(l , r) ; 

			return MinST(Rank[l] + 1 , Rank[r]) ; 
		}

		inline void GetHeight() {
			for (int i = 2 ; i <= n ; ++ i) lg[i] = lg[i >> 1] + 1 ; 
			for (int i = 1 , k = 0 ; i <= n ; ++ i) {
				if (!Rank[i]) continue ; 
				if (k) k -- ; 

				while (s[i + k] == s[SA[Rank[i] - 1] + k]) ++ k ; 
				ST[0][Rank[i]] = k ; 
			}
			for (int j = 1 ; j <= lg[n] ; ++ j) {
				for (int i = 1 ; i <= n - (1 << j) + 1 ; ++ i) {
					ST[j][i] = min(ST[j - 1][i] , ST[j - 1][i + (1 << (j - 1))]) ; 
				}
			}
		}
	}
} using namespace CharacterString ; 
using namespace SuffixArray ; 

namespace Chairman_Tree {
	#define lson(x) t[x].son[0]
	#define rson(x) t[x].son[1]
	#define data(x) t[x].data
	#define mid ((l + r) >> 1)

	struct Node {
		int son[2] , data ; 
	} t[N * 200] ; int root[N] , numbol ; 

	void updata(int &now , int id , int l , int r , int x) {
		t[now = ++ numbol] = t[id] ; 

		if (l == r) return t[now].data ++ , void() ; 
		if (x <= mid) updata(lson(now) , lson(id) , l , mid , x) ; 
		else updata(rson(now) , rson(id) , mid + 1 , r , x) ; 

		t[now].data = t[lson(now)].data + t[rson(now)].data ; 
	}

	bool check(int fir , int sec , int l , int r , int x , int y) {
		if (x <= l && r <= y) return (data(fir) - data(sec) != 0) ; 
		
		bool flag = 0 ; 
		if (x <= mid) flag |= check(lson(fir) , lson(sec) , l , mid , x , y) ; 
		if (y > mid) flag |= check(rson(fir) , rson(sec) , mid + 1 , r , x , y) ; 
		
		return flag ; 
	}

	#undef mid
} using namespace Chairman_Tree ; 

pair <int , int> Find(int now , int x) {
	int left = 1 , right = now - 1 , ans1 = now , ans2 = now ; 

	while (left <= right) {
		int mid = (left + right) >> 1 ; 

		if (LCP(SA[mid] , SA[now]) >= x) {
			ans1 = mid ; 
			right = mid - 1 ; 
		} else left = mid + 1 ; 
	}
	
	left = now + 1 , right = n ; 

	while (left <= right) {
		int mid = (left + right) >> 1 ; 

		if (LCP(SA[mid] , SA[now]) >= x) {
			left = mid + 1 ; 
			ans2 = mid ; 
		} else right = mid - 1 ; 
	}

	return make_pair(ans1 , ans2) ; 
}

signed main() {
	// freopen("1.in" , "r" , stdin) ; 
	// freopen("1.out" , "w" , stdout) ; 
	ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ; 
	cin >> n >> M ; 
	for (int i = 1 ; i <= n ; ++ i) cin >> s[i] ; 
	Suffix_Array() ; GetHeight() ; 
	int ans = 0 ; pair <int , int> mp ; 

	for (int i = 1 ; i <= n ; ++ i) updata(root[i] , root[i - 1] , 1 , n , Rank[i]) ; 
	for (int i = 1 , w , x , y , z ; i <= M ; ++ i) {
		cin >> w >> x >> y >> z ; ans = 0 ; 
		int left = 1 , right = min(x - w + 1 , z - y + 1) ; 

		while (left <= right) {
			int mid = (left + right) >> 1 ; 
			mp = Find(Rank[y] , mid) ; int L = mp.first , R = mp.second ; 

			if (check(root[x - mid + 1] , root[w - 1] , 1 , n , L , R)) {
				left = mid + 1 ; ans = mid ; 
			} else right = mid - 1 ; 
		}

		cout << ans << '\n' ; 
	}
}
posted @ 2024-09-07 19:49  HANGRY_Sol&Cekas  阅读(25)  评论(1编辑  收藏  举报