【字符串杂谈】
前言
树真的花了好久。。
还剩下\(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");
}
阿狸的自动机
先咕一下