SAM 从入门到入土

1 CF204E
求n个字符串的一个字符串集合,对于每个字符串求有多少个子串是这个字符串集合中至少k个字符串的子串。

做法

我们进行线段树合并出每个 状态所包含的是哪个串 统计出是否大于k
如果 大于k我们直接 加上len的贡献 如果没有标记的话就是 最近的有标记的父亲

2 CF666E
给出一个串s
和几个串ti
求出串s [l,r] 在串 ti出现次数最大
相同输出最左边的

做法

线段树合并 每个节点代表的是 该点 在哪个字符串中出现(出现的位置) 并且维护出出现的次数 那么 我们进行合并的时候考虑二维就可以了
我们可以倍增锁定位置

3 P3975 [TJOI2015]弦论

做法

我们分情况讨论
如果t=0
那么就是本质不同的串 我们每个点的点权为 1
如果t=1
包含位置不同的串 我们每个点的点权可以dfs 或者基数排序求
我们dfs(也可以dag上dp) 一遍 求出 每个状态和 表示 点u能到达的点的数量
那么我们最后递归处理的时候 如果k<=siz[1] 直接返回-1
否则我们贪心的递归输出

4 P4070 [SDOI2016]生成魔咒

做法

每增加一个串 就会增加 len[np]-len[tr[np].fa] 个字符串
5CF235C Cyclical Quest

给定一个主串S和n个询问串,求每个询问串的所有循环同构在主串中出现的次数总和。

做法

很套路的东西 我们可以直接在t串后面接一个串 然后我们跑t串 每次倍增
找到 加入 t[i] 后 长度为l的状态 直接给该状态 打上标记 加上该状态贡献
注意如果 没有找到 下一个状态记得将 该指针置为1

6P4094 [HEOI2016/TJOI2016]字符串

做法

给定串s 和q 个询问
每次问s[a…b]的所有子串和s[c…d]的最长公共前缀最大值
将串反转处理 那么我们每次可以二分出一个mid 倍增可以找到s[i-mid+1,i]
预处理的话线段树合并求出每个点的endpos集合
那么我们只需要判断一下endpos集合里包含 [a+mid-1,b]不是[a,b]
如果小于a+mid-1 那么这个字符串的左端有可能小于a 就错了
还有线段树合并的时候如果 不建立新节点的写法可能会tle

7葫芦的考验之定位子串2.0
求子串 s[l ,r] 在 s[x,y]的出现次数

做法

上个题的降维版本
线段树合并预处理endpos
我们倍增可以锁定 s[l,r]的状态
直接查询 [x+mid-1,y]

常用的 线段树合并

struct seg {
	void pushup(int x) {
		sum[x] = sum[ls[x]] + sum[rs[x]];
	}
	void change(int i, int l, int r, int x) {
		if(!i) i = ++idx;
		if(l == r) {
			sum[x] = 1;
			return ;
		}
		int mid = l + r >> 1;
		if(x <= mid) change(ls[i], l, mid, x);
		else change(rs[i], mid + 1, r, x);
		pushup(i);
	}
	int  query(int i, int l, int r, int ql, int qr) {
		if(l <= ql && qr <= r) {
			return sum[x];
		}
		int mid = l + r >> 1;
		int ans = 0;
		if(l <= mid) ans += query(ls[i], l, mid, ql, qr);
		if(r > mid)  ans += query(rs[i], mid + 1, r, ql, qr);
		return ans;
	}
	int  merge(int x, int y, int l, int r) {
		if(!x || !y) return x + y;
		int p = ++idx;
		int mid = l + r >> 1;
		if(l == r) {
			sum[p] = sum[x] | sum[y];
			return p;
		}
		ls[p] = merge(ls[x], ls[y], l, mid);
		rs[p] = merge(rs[x], rs[y], mid + 1, r);
		pushup(p);
		return p;
	}
};

8"蔚来杯"2022牛客暑期多校训练营3H
板子题
贴代码了
t串在s串上跑
直接st查询最小值 即可

#include 
using namespace  std;
//#define  int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
#define fi first
#define se second
#define pb  push_back
#define inf 1ll<<62
#define endl "\n"
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define de_bug(x) cerr << #x << "=" << x << endl
#define all(a) a.begin(),a.end()
#define IOS   std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define  fer(i,a,b)  for(int i=a;i<=b;i++)
#define  der(i,a,b)  for(int i=a;i>=b;i--)
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
int n, m , k;
string s;
int cnt = 1;
int last = 1;

struct node {
	int fa, len;
	int son[26] = {};
} tr[N];
map<int, int>mp;
int idx;
int siz[N];
void extend (int c) {
	int p = last, np = last = ++cnt;
	tr[np].len = tr[p].len + 1;
	mp[++idx] = np;
	siz[np] = 1;
	for(; p && !tr[p].son[c];  p = tr[p].fa) tr[p].son[c] = np;
	if(!p) tr[np].fa = 1;
	else {
		int q = tr[p].son[c];
		if(tr[q].len == tr[p].len + 1)tr[np].fa = q;
		else {
			int nq = ++cnt;
			tr[nq] = tr[q];
			tr[nq].len = tr[p].len + 1;
			tr[q].fa = tr[np].fa = nq;
			for(; p && tr[p].son[c] == q; p = tr[p].fa) tr[p].son[c] = nq;
		}
	}
}
ll mi[N][20];
ll sum[N];
int lg[N];

ll query(int l, int r) {
	int t = lg[r - l + 1];
	return min(mi[l][t], mi[r - (1 << t) + 1][t]);
}
void solve() {
	for(int i = 2; i < N; i++) {
		lg[i] = lg[i >> 1] + 1;
	}
	cin >> n >> m >> k;
	string s;
	cin >> s;
	for(auto c : s) {
		extend(c - 'a');
	}
	for(int i = 1; i <= m; i++) {
		cin >> sum[i];
		sum[i] += sum[i - 1];
		mi[i][0] = sum[i - 1];
	}

	for(int i = 1; i <= 20; i++)
		for(int j = 1; j + (1 << i) - 1 <= m; j++) {
			mi[j][i] = min(mi[j][i - 1], mi[j + (1 << (i - 1))][i - 1]);
		}
	while(k--) {
		string t;
		cin >> t;
		ll ans = 0;
		int p = 1;
		int l = 0;
		for(int i = 0; i < m; i++) {
			while(p && !tr[p].son[t[i] - 'a']) {
				p = tr[p].fa;
				l = tr[p].len;
			}
			if(tr[p].son[t[i] - 'a']) {
				l++;
				p = tr[p].son[t[i]-'a'];
			} else  l = 0, p = 1;

			if(l)
				ans = max(ans, sum[i + 1] - query(i - l + 2, i + 1));
		}
		cout << ans << endl;
	}
}
int main() {
	IOS;
	int _ = 1;
	//cin>>_;
	while( _-- )
		solve();
}



9CF802I Fake News (hard)
sam板子 直接求出每个状态的大小那么 这个状态的贡献就是 siz[u]*siz[u]*len[u]-len[tr[u].fa]
10CF1037H Security
线段树合并 求个每个状态的endpos集合
因为字典序要严格大于t串
匹配t串 求出在S[L,R]出现过的最长串的状态
然后我们贪心的考虑 如果刚好匹配成功 那么 我们只需下一位从 0开始就行
否则我们用一个栈去维护 从后往前 依次 去枚举 比当前位大并且 出现在s[l,r]
的串

21 沈阳String Problem
贪心走 字典序大的 第一走到的肯定是最大后缀

#include 
using namespace  std;
//#define  int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
#define fi first
#define se second
#define pb  push_back
#define inf 1ll<<62
#define endl "\n"
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define de_bug(x) cerr << #x << "=" << x << endl
#define all(a) a.begin(),a.end()
#define IOS   std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define  fer(i,a,b)  for(int i=a;i<=b;i++)
#define  der(i,a,b)  for(int i=a;i>=b;i--)
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n, m , k;
string  s;
string  t;
struct node {
	int len, fa;
	int son[26];
	int pos;
} tr[N * 2];
int cnt = 1;
int last = 1;
int ans[N];
int pos[N];

void extend (int c) {
	int p = last, np = last = ++cnt;
	tr[np].pos = tr[np].len = tr[p].len + 1;
	for(; p && !tr[p].son[c];  p = tr[p].fa) tr[p].son[c] = np;
	if(!p) tr[np].fa = 1;
	else {
		int q = tr[p].son[c];
		if(tr[q].len == tr[p].len + 1)tr[np].fa = q;
		else {
			int nq = ++cnt;
			tr[nq] = tr[q];
			tr[nq].len = tr[p].len + 1;
			tr[q].fa = tr[np].fa = nq;
			for(; p && tr[p].son[c] == q; p = tr[p].fa) tr[p].son[c] = nq;
		}
	}
}
int vis[N * 2];
void sol(int x, int l) {
	vis[x] = 1;
	for(int i = 25; i >= 0; i--) {
		if(!tr[x].son[i] || vis[tr[x].son[i]] )continue;
		sol(tr[x].son[i], l + 1);
	}
	if(ans[tr[x].pos ] == 0) {
		ans[tr[x].pos ] = tr[x].pos - l + 1 ;
	}
}
void solve() {
	cin >> s;
	for(auto c : s) {
		extend(c - 'a');
	}
	sol(1, 0);

	for(int i = 1; i <= (int)s.size(); i++ ) {
		cout << ans[i] << " " << i << " \n";
	}
	//cout << endl;
}
int  main() {
	IOS;
	int _ = 1;
	//cin>>_;
	while( _-- )
		solve();
}

/*
potato
1 1
1 2
3 3
3 4
3 5
5 6

*/





你可能感兴趣的:(字符串,数据结构,算法)