【字符串杂谈】

前言

树真的花了好久。。
还剩下\(LCT\)没有学,等最后一周板刷联合省选,遇到的话再学了
现在是字符串时间
\(Hash\)\(Kmp\)自然不用说了

\(Machacher\)

挂个板子好了

#include<iostream>
#include<cstdio>
#define ll long long
#define N 30000000

char a,s[N];
ll len = 0,m,ans[N],fans;

int main(){
	s[0] = '~';
	a = getchar();
	while(a <= 'z' && a >= 'a'){
		s[++len] = '|';
		s[++len] = a;
		a = getchar();
	}
	s[++len] = '|';
	for(int i = 1;i <= len;++i){
		if(m + ans[m] >= i)
		ans[i] = std::min(m + ans[m] - i,ans[m * 2 - i]);
		while(s[i - ans[i]] == s[i + ans[i]]) ans[i] ++ ;
		if(ans[i] + i > m + ans[m])m = i;
		if(fans < ans[i])
		fans = ans[i];
	} 
	std::cout<<fans - 1;
}

[THUPC2018]绿绿和串串

[THUPC2018]绿绿和串串
考虑对于一个串是否可以进行判定,要么以这个字符为中心的回文串的最右端到\(T\)的最右端,要么以这个字符为中心的回文串的最右端属于可以的答案,以及以这个字符为中心的回文串的最左端为1

#include<iostream>
#include<cstdio>
#define ll long long
#define N 1000005

ll ans[N << 1];
char s[N << 1];
bool y[N << 1];

int t,len = 0;

void ma(){
	ll m = 0;
	len = 0;
	char c = getchar();
	while(c > 'z' || c < 'a')
	c = getchar();
	s[len] = '~';
	while(c <= 'z' && c >= 'a')
	s[++len] = '|',s[++len] = c,c = getchar();
	s[++len] = '|';
	s[len + 1] = '\0';
	for(int i = 1;i <= len;++i){
		ans[i] = 0;
		if(m + ans[m] >= i)
		ans[i] = std::min(m + ans[m] - i,ans[(m << 1) - i]);
		while(s[i + ans[i]] == s[i - ans[i]]) ++ ans[i];
		if(i + ans[i] > m + ans[m])
		m = i;
	}
}

int main(){
	scanf("%d",&t);
	while(t -- ){
		ma();
		for(int i = len;i >= 1;--i){
			y[i] = 0;
			y[i] = ((i + ans[i] - 1) == len) || ((i - ans[i] + 1 == 1) && (y[i + ans[i] - 2]));
		}
		for(int i = 2;i <= len;i += 2)
		if(y[i])
		printf("%d ",i >> 1);
		puts("");
	}
}

最小表示法

最小表示法
考虑对暴力进行一个优化,如果对于两个串\(s[i....],s[j....]\)\(k\)处不同,那么如果\(s[i + k] > s[j + k]\)对于\(s[i + 1..],s[i + 2...]...s[i + k...]\)就都没有\(s[j]\)优,那么\(i = i + k + 1\)如果相反那么我们把\(i,j\)调过来就行了,我们始终维护\(j\)为最优解

#include<iostream>
#include<cstdio>
#define ll long long
#define N 300005

ll n;

ll a[N << 1];

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i)
	scanf("%lld",&a[i]);
	for(int i = n + 1;i <= 2 * n;++i)
	a[i] = a[i - n];
	int j = 1,i = 2,k,tmp;
	while(i <= n){
		k = 0;
		while(a[i + k] == a[j + k])
		k += 1;
		if(a[i + k] < a[j + k])
		tmp = i,i = std::max(i + 1,j + k + 1),j = tmp;
		else
		i = i + k + 1;
	}
	for(int l = 1;l <= n;++l)
	std::cout<<a[j + l - 1]<<" ";
}

[TJOI2017]DNA

[TJOI2017]DNA
考虑进行暴力,因为要进行暴力,那么我们需要求出\(LCP\)
现有一个结论字典序为\(u,v\)的两个后缀,他们的\(LCP\)\(min(height[u...v])\)倍增和线段树都能做。

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e6 + 5;
int rk[maxn], RK[maxn];
int sa[maxn], SA[maxn];
int bac[maxn], n, h[maxn], height[maxn];
int rmq[maxn][18], bin[1 << 18];
char str[maxn];

void getsa(char *str, int n, int alp = 256) {
    for (int i = 0; i <= alp; i++) bac[i] = 0;
    for (int i = 1; i <= n; i++) ++bac[str[i]];
    for (int i = 1; i <= alp; i++) bac[i] += bac[i - 1];
    for (int i = 1; i <= n; i++) sa[bac[str[i]]--] = i;
    for (int i = 1; i <= n; i++) rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);

    for (int p = 1; p <= n; p <<= 1)
    {
        for (int i = 1; i <= n; i++) bac[rk[sa[i]]] = i;
        for (int i = n; i >= 1; i--) if (sa[i] > p) SA[bac[rk[sa[i] - p]]--] = sa[i] - p;
        for (int i = n; i > n - p; i--) SA[bac[rk[i]]--] = i;
        #define comp(x, y) (rk[x] != rk[y] || rk[x + p] != rk[y + p])
        for (int i = 1; i <= n; i++) RK[SA[i]] = RK[SA[i - 1]] + comp(SA[i], SA[i - 1]);
        for (int i = 1; i <= n; i++) sa[i] = SA[i], rk[i] = RK[i];
        if (rk[sa[n]] >= n) return ;
    }
}
void geth(char *str, int n) {
    for (int i = 1; i <= n; i++)
    {
        int j = sa[rk[i] - 1], k = max(0, h[i - 1] - 1);
        while (str[i + k] == str[j + k] && str[i + k]) ++k;
        h[i] = height[rk[i]] = k;
    }
    for (int j = 0; j < 18; j++)
    for (int i = 1 << j; i < (1 << j + 1); i++) bin[i] = j;
    for (int i = 1; i <= n; i++) rmq[i][0] = height[i];
    for (int j = 1; j < 18; j++)
    for (int i = 1 << j; i <= n; i++)
        rmq[i][j] = min(rmq[i - (1 << j - 1)][j - 1], rmq[i][j - 1]);
}
int lcp(int x, int y) {
    int u = rk[x], v = rk[y];
    if (u > v) swap(u, v);
    int j = bin[v - u];
    return min(rmq[v][j], rmq[u + (1 << j)][j]);
}

int main()
{
    int T; for (scanf("%d", &T); T--; )
    {
        scanf("%s", str + 1);
        n = strlen(str + 1);
        str[n + 1] = '@';
        int j = n + 1; 
        scanf("%s", str + n + 2);
        n = strlen(str + 1);
        int m = n - j; 
        getsa(str, n); geth(str, n);
        int ans = 0;
        for (int i = 1; i + m <= j; i++)
        {
            int p = lcp(i, j + 1) + 1;
            for (int k = 1; k <= 3 && p <= m; k++)
                p += lcp(i + p, j + p + 1) + 1;
            ans += (p > m);
        }
        printf("%d\n", ans);
    }
}

\(AC\)自动机

挂板子

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

ll n,cnt;
int to[1000005][50],end[1000005],fail[1000005];
char a[1000005];

void insert(){
	ll now = 0;
	ll n = strlen(a + 1);
	for(int i = 1;i <= n;++i){
		if(!to[now][a[i] - 'a'])
		to[now][a[i] - 'a'] = ++ cnt ;
		now = to[now][a[i] - 'a'];
	}
	end[now] ++ ;
}

std::queue<int>	QWQ ;

void get(){
	for(int i = 0;i < 26;++i)
	if(to[0][i]) fail[to[0][i]] = 0,QWQ.push(to[0][i]);
	while(! QWQ.empty()){
		ll u = QWQ.front();QWQ.pop();
		for(int i = 0;i < 26;++i)
		if(to[u][i]) fail[to[u][i]] = to[fail[u]][i],QWQ.push(to[u][i]);
		else to[u][i] = to[fail[u]][i]; 
	}
}

int query(){
	ll len = strlen(a + 1),now = 0,ans = 0;
	for(int i = 1;i <= len;++i){
		now = to[now][a[i] - 'a'];
		for(int t = now;t && end[t] != -1;t = fail[t]) ans += end[t],end[t] = -1;
	}
	return ans;
}

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i){
		scanf("%s",a + 1);
		insert();
	}
	get();
	scanf("%s",a + 1);
	std::cout<<query()<<std::endl;
}

[POI2000]病毒

[POI2000]病毒
考虑建完\(AC\)自动机,那么此时自动机是一个有向图,那么如果出现环就是一个安全的代码

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

ll n,cnt;

using std::queue;

char num[30005];

ll trie[40000][5],fail[40000];

bool end[40000];

void insert(){
	ll now = 0;
	ll len = strlen(num + 1);
	for(int i = 1;i <= len;++i){
		if(!trie[now][num[i] - '0'])
		++cnt,trie[now][num[i] - '0'] = cnt;;
		now = trie[now][num[i] - '0'];
	}
	end[now] = 1;
}

queue<int>QWQ;

void get_fail(){
	for(int i = 0;i <= 1;++i)
	if(trie[0][i]) fail[trie[0][i]] = 0,QWQ.push(trie[0][i]);
	while(!QWQ.empty()){
		int u = QWQ.front();QWQ.pop();
		for(int i = 0;i <= 1;++i){
		if(trie[u][i]) fail[trie[u][i]] = trie[fail[u]][i],QWQ.push(trie[u][i]);
		else trie[u][i] = trie[fail[u]][i];
		if(end[trie[fail[u]][i]])
		end[trie[u][i]] = 1;
		}
	}
}

bool ins[40000],used[40000];

bool get(ll now){
	ins[now] = 1;
	for(int i = 0;i < 2;++i){
		int v = trie[now][i];
		if(ins[v]) return 1;
		if(used[v] || end[v]) continue;
		used[v] = 1;
		if(get(v)) return 1;
	}
	ins[now] = 0;
	return 0;
} 

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i){
		scanf("%s",num + 1);
		insert();
	}
	get_fail();	
	if(get(0))
	puts("TAK");
	else
	puts("NIE");
}

阿狸的自动机

先咕一下

posted @ 2021-03-25 16:10  fhq_treap  阅读(62)  评论(0编辑  收藏  举报