NFLS 字符串题单笔记(未完结)

POI2010 Antisymmetry

对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。

现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

manacher板子,写就完了

#include <bits/stdc++.h>
#define ull long long
const int maxn = 1e7;
char SS1[maxn], S[maxn], to[500];
int n, len[maxn], tot = 1;
signed main() {
    scanf("%d%s", &n, SS1 + 1);
    S[0] = '$', S[1] = '#';
    for (register int i = 1; i <= n; ++i) S[++tot] = SS1[i], S[++tot] = '#';
    to['1'] = '0', to['0'] = '1', to['#'] = '#', to['$'] = '$';
    int pos = 1, mx = 1;
    ull ans = 0;
    for (register int i = 1; i <= tot; i += 2) {
        len[i] = (i < mx ? std::min(mx - i, len[(pos << 1) - i]) : 1);
        while (S[i + len[i]] == to[S[i - len[i]]]) len[i]++;
        if (len[i] + i > mx) {
            mx = len[i] + i;
            pos = i;
        }
        ans += len[i] >> 1;
    }
    printf("%llu\n", ans);
    return 0;
}


Bovine Genomics

FJ拥有有斑点的牛和没有斑点的牛。他刚刚完成了牛基因的一门课程,他相信牛身上的斑点是由牛的基因突变引起的。FJ花费了巨大的代价,将他的牛的基因组排序。每个基因组都是长度为的字符串。当他把牛的基因组排列起来的时候,他得到了一个像下面这样的表格(,):

位置: 1 2 3 4 5 6 7 8
斑点牛1: A A T C C C A T
斑点牛2: A C T T G C A A
斑点牛3: G G T C G C A A
普通牛1: A C T C C C A G
普通牛2: A C T C G C A T
普通牛3: A C T T C C A T
如果在某一段区间内,斑点牛和普通牛没有相同的基因段(即两组集合没有交集),那么FJ就能通过这段区间分辨两种牛。

请你帮FJ找出最短的满足条件的区间。

对每个字符串求哈希值。
\(n^2\)枚举整体的每个子串集合,然后由于求出哈希了,要判断两个字串的哈希集合是否有重复的。可以用set或者排序之后跑一遍双指针,复杂度nlogn。\(n^3 log n\)显然会T。考虑把枚举字串集合改为二分答案,二分答案的长度,然后每二分一次都check一遍。复杂度\(n^2 log_2 n\)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int base = 29;
const int LEN = 514;
int n, m, N;
struct node {
    char str[LEN];
    ull f[LEN];
} s[LEN << 1];
ull p[LEN];
inline bool check(int st, int ed, int len) {
    set<ull> s1;
    set<ull> s2;
    for (int i = 1; i <= N; ++i) {
        if (i <= n)
            s1.insert(s[i].f[ed] - s[i].f[st - 1] * p[len]);
        else
            s2.insert(s[i].f[ed] - s[i].f[st - 1] * p[len]);
    }
    int len1 = s1.size(), len2 = s2.size(), len3;
    s1.insert(s2.begin(), s2.end());
    len3 = s1.size();
    if (len1 + len2 > len3)
        return false;
    return true;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    p[0] = 1;
    for (int i = 1; i <= 500; ++i) p[i] = (ull)(p[i - 1] * base);
    N = (n << 1);
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> s[i].str[j];
            s[i].f[j] = s[i].f[j - 1] * base + s[i].str[j] - 'A' + 1;
        }
    }
    int l = 1, r = m, mid;
    while (l < r) {
        bool flag = false;
        mid = (l + r) >> 1;
        for (int st = 1; st + mid - 1 <= m; ++st) {
            int ed = st + mid - 1;
            if (check(st, ed, mid)) {
                flag = true;
                break;
            }
        }
        if (flag)
            r = mid;
        else
            l = mid + 1;
    }
    printf("%d", l);
    return 0;
}

反例 贴一下卢的厌氧代码,不开O0过不去

#include <iostream>
#include <algorithm>
#pragma GCC optimize("O0")
using namespace std;
typedef long long ll;
const int N = 1010;
const ll ba = 131, p = 1e9 + 7;

int n, m;
ll h[N], ha[N][N], hb[N][N];
ll A[N], B[N];
char a[N][N], b[N][N];

bool check(int l, int r) {
    for (int i = 1; i <= n; ++i) A[i] = (p + ha[i][r] - (ha[i][l - 1] * h[r - l + 1])) % p;
    for (int i = 1; i <= n; ++i) B[i] = (p + hb[i][r] - (hb[i][l - 1] * h[r - l + 1])) % p;
    sort(A + 1, A + n + 1), sort(B + 1, B + n + 1);
    int len = 1;
    bool flag = 1;
    for (int i = 1; i <= n; ++i) {
        while (A[i] > B[len]) ++len;
        if (A[i] == B[len]) {
            flag = 0;
            break;
        }
    }
    return flag;
}

int main() {
    cin >> n >> m;
    h[0] = 1;
    for (int i = 1; i <= m; ++i) h[i] = h[i - 1] * ba % p;
    for (int i = 1; i <= n; ++i) {
        cin >> (a[i] + 1);
        for (int j = 1; j <= m; ++j) ha[i][j] = (ha[i][j - 1] * ba + (a[i][j] - 'A')) % p;
    }
    for (int i = 1; i <= n; ++i) {
        cin >> (b[i] + 1);
        for (int j = 1; j <= m; ++j) hb[i][j] = (hb[i][j - 1] * ba + (b[i][j] - 'A')) % p;
    }
    int l = 1, r = m, mid;
    bool flag = 0;
    while (l < r) {
        mid = (l + r) >> 1;
        flag = 0;
        for (int i = 1; i + mid - 1 <= m; ++i)
            if (check(i, i + mid - 1)) {
                flag = 1;
                break;
            }
        if (flag)
            r = mid;
        else
            l = mid + 1;
    }
    cout << l << endl;
    return 0;
}

friends

有三个好朋友喜欢在一起玩游戏,A君写下一个字符串S,B君将其复制一遍得到T,C君在T的任意位置(包括首尾)插入一个字符得到U.现在你得到了U,请你找出S.

求出哈希。
枚举插入字符的点,判断前面s和后面的s是否一样。
技巧:字符串\(S _{1...n}\) 现在有\(hash1_{1...a}, hash2_{a+2...n}\)
那么\(hash1,hash2\)拼一起的哈希值就是$hash1 * base^{hash2.length()+1} + hash2 $

似乎在梦中见过的样子

一个长度为 n 的字符串,所有形似于 A+B+A 的子串都是它的替身 , 且len(A)>=k,len(B)>=1 (位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一子串),求它的替身的数量.

枚举A串的所有后缀,匹配,每次匹配成功一个字符都ans[当前模式串匹配到的长度(大于等于k)]++,ans[i]代表字串长度为i时匹配成功的字符串数量,要考虑两个匹配成功串的尾首间距小于1的情况,On枚举所有后缀匹配,复杂度是n^2的,最后统计答案。显然这玩意又不好想又不好写,但我还是写了,显然没调出来。还是贴一下代码吧:

#include<bits/stdc++.h>
using namespace std;
constexpr int maxn = 10;
vector<int> getnext(char s[]){
	vector<int> next(maxn);
	int len = strlen(s);
	next[0] = -1;
	int j = 0;
	for(int i = 1;i < len;i++){
		j = next[i-1];
		while(s[i] != s[j+1] && j >= 0)
			j = next[j];
		if(s[j+1] == s[i]) next[i] = ++j;
		else next[i] = -1;
	}
	return next;
	/*
	0123456
	ababaca
	0001
	*/
}
int ans[maxn];
int sub[maxn];
char S[maxn];
void kmp(char s[],char ms[]){
	vector<int> next = getnext(ms);
	bool vis[maxn];
	int j = -1;
	int lens = strlen(s),lenms = strlen(ms);
	for(int i = 0;i < lens;i++){
		while(j >= 0&& ms[j+1] != s[i]){
			j = next[j];
		}
		if(ms[j+1] == s[i]){
			++j;
			vis[i] = true;
			if(vis[i-1] || vis[i+1]) sub[j+1]++;
				ans[j+1]++;
			
		}
		if(j == lenms){
			j = next[j];
		}
	}
}
int k;
int main(){
	cin>>S;
	cin>>k;
	
	int len = strlen(S);
	for(char *i = S,j = 0;j < len-1;i++,j++){
		kmp(S,i);
	}
	int res = 0;
	for(int i = k;i < len;i++){
		res += ans[i] * (ans[i]-1) - sub[i];
	}
	cout<<res<<endl;
	return 0;
}


现在来说说正解。

image

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int k,n;
ll ans;
int nxt[20000],f[20000];
char s[20000];
void get_nxt(int x) {
	nxt[1]=0;
	for(int i=2,j=0;i<=n-x;i++) {
		while(j>0&&s[i+x]!=s[j+x+1]) j=nxt[j];
		if(s[j+x+1]==s[i+x]) j++;
		nxt[i]=j;
		if(f[j]) f[i]=f[j];
		else if(j>=k) f[i]=j;
		else f[i]=0;
		if(f[i]&&(f[i]<<1)<i) ans++;
	}
}
int main() {
	scanf("%s%d",s+1,&k);
	n=strlen(s+1);
	for(int i=1;i<=n;i++) {
		get_nxt(i-1);
	}
	printf("%lld\n",ans);
	return 0;
}


posted @ 2024-11-18 21:59  Dreamers_Seve  阅读(5)  评论(1编辑  收藏  举报