【ybtoj高效进阶 21175】DNA 序列(SAM)

DNA 序列

题目链接:ybtoj高效进阶 21175

题目大意

给你一个由四种字符组成的字符串,然后问你长度为 k 的子串中出现次数最多的串的出现次数。
1<=k<=10

思路

其实因为 1 ⩽ k ⩽ 10 1\leqslant k\leqslant 10 1k10,而且只有四种字符,所以我们可以直接暴力搞。

但我直接用 SAM 了。
(因为用 SAM 也是模板题)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

struct node {
    
    
	int son[4], len, fa;
	ll sz;
}d[10000001];
char s[5000001];
int sn, k, lst, tot;
ll ans;

int ck(char c) {
    
    
	if (c == 'A') return 0;
	if (c == 'T') return 1;
	if (c == 'G') return 2;
	return 3;
}

void insert(int x) {
    
    
	int p = lst;
	int np = ++tot;
	lst = np;
	d[np].len = d[p].len + 1;
	d[np].sz = 1;
	for (; p && !d[p].son[x]; p = d[p].fa) d[p].son[x] = np;
	if (!p) d[np].fa = 1;
		else {
    
    
			int q = d[p].son[x];
			if (d[q].len == d[p].len + 1) d[np].fa = q;
				else {
    
    
					int nq = ++tot;
					d[nq] = d[q];
					d[nq].sz = 0;
					d[nq].len = d[p].len + 1;
					d[q].fa = d[np].fa = nq;
					for (; p && d[p].son[x] == q; p = d[p].fa) d[p].son[x] = nq;
				}
		}
}

int tong[5000001], xl[10000001];

void DP() {
    
    
	for (int i = 1; i <= tot; i++)
		tong[d[i].len]++;
	for (int i = 1; i <= sn; i++)
		tong[i] += tong[i - 1];
	for (int i = 1; i <= tot; i++) {
    
    
		xl[tong[d[i].len]--] = i;
	}
	
	for (int i = tot; i >= 1; i--)
		d[d[xl[i]].fa].sz += d[xl[i]].sz;
}

int main() {
    
    
//  freopen("dna.in", "r", stdin);
//	freopen("dna.out", "w", stdout);
	
	scanf("%s", s + 1);
	sn = strlen(s + 1);
	scanf("%d", &k);
	
	lst = tot = 1;
	for (int i = 1; i <= sn; i++) {
    
    
		insert(ck(s[i]));
	}
	
	DP();
	
	for (int i = 1; i <= tot; i++) {
    
    
		if (d[i].len >= k && d[d[i].fa].len + 1 <= k) {
    
    
			ans = max(ans, d[i].sz);
		}
	}
	printf("%lld", ans);
	
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43346722/article/details/121130374