10.3考试t3(similarity)solution
题目描述
给你一个小写字母组成的字符串,求出一个这样一个最长子串,满足:
- 其在原串的不同位置出现了两次(起始位置不同,可以部分重叠)
- 其反串在原串中出现了一次(与前面两次无关)
数据范围
字符串长度 \(n \le 5e4\)
方法
考虑前缀哈希 + 二分。
如果说假设满足要求的子串为 \(s\) ,若存在 \(s\) 的长度等于 \(n\) ,那一定存在长度小于 \(s\) 的子串满足要求(比如 \(s\) 的子串),所以不难看出本题答案是具有单调性的。
现在我们可以二分答案,只需考虑 check() 的写法。
因为我们二分了满足条件的字串的长度(假设为\(l\)),我们考虑开个桶,统计每个字符串出现的个数。再在反串中验证每个长度为 \(l\) 的串在正串中出现的次数是否有大于等于 \(2\) 的。
具体的实现有很多种,可以开一个 map <string, int>, 或者前缀哈希加桶。我选择了最垃圾的方法是 map <int, int> 加上一个类似st表的哈希,用倍增将子串的哈希值拼起来。
时间复杂的: \(O (n log^k n)\) ,由于方法不同 \(k\) 可能在 1~3 中任意的数,但对于 \(5e4\) 的数据是绝对能过得。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define usd unsigned
#define el cout << '\n'
#define lowbit(x) (x & (-x))
const int ranmod = 1e7;
#define random ((rand() * rand()) % ranmod)
#define AC return 
#define AK return 0
#define YS cout << "YES"
#define NO cout << "NO"
#define Ys cout << "Yes"
#define No cout << "No"
#define ys cout << "yes"
#define no cout << "no"
#define ls(i) ch[i][0]
#define rs(i) ch[i][1]
#define debug(num) cerr << #num << ' ' << num << '\n'
// void init();
void main_();
signed main() {
	ios :: sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	// freopen("similarity.in", "r", stdin);
	// freopen("similarity.out", "w", stdout);
	int t = 1;
	// cin >> t;
	while(t--) {
		// init();
		main_();
	}
	AK;
}
const int mod = 95225987, maxn = 5 * 1e5 + 18;
const usd int p = 2591;
string s, s1;
int n; 
usd int hsh[maxn][16], hsh1[maxn][16], nxt[maxn][16];
map <usd int, long> cnt;
usd int ksm(usd int a, usd int b) {
    usd int res = 1;
    do {
        if(b & 1) res *= a;
        a *= a, b = b >> 1;
    } while(b);
    return res;
}
bool check(int num) {
    // debug(num);
    cnt.clear();
    for(int i = 1; i <= n; i++) {
        if(i + num - 1 > n) break;
        usd int fl = i, hs = 0;
        for(int k = 15; k >= 0; k--) {
            if(fl + (1ll << k) <= i + num) {
                hs = hs * ksm(p, (1ll << k)) + hsh[fl][k];
                fl += (1ll << k);
            }
        }
        // debug(hs);
        cnt[hs]++;
    }
    for(int i = 1; i <= n; i++) {
        if(i + num - 1 > n) break;
        usd int fl = i, hs = 0;
        for(int k = 15; k >= 0; k--) {
            if(fl + (1ll << k) <= i + num) {
                hs = hs * ksm(p, (1ll << k)) + hsh1[fl][k];
                fl += (1ll << k);
            }
        }
        if(cnt[hs] >= 2) {
            // debug(num), debug(i);
            // debug(hs), debug(cnt[hs]);
            return true;
        }
    }
    return false;
}
void main_() {
    cin >> s;
    n = s.length();
    for(int i = n - 1; i >= 0; i--) s1 += s[i];
    s = ' ' + s;
    s1 = ' ' + s1;
    for(int i = 1; i <= n; i++) hsh[i][0] = s[i], hsh1[i][0] = s1[i];
    for(int k = 1; k <= 15; k++) {
        for(int i = 1; i <= n; i++) {
            if(i + (1ll << (k - 1)) - 1 > n) continue;
            hsh[i][k] = hsh[i][k - 1] * ksm(p, 1ll << (k - 1)) + hsh[i + (1ll << (k - 1))][k - 1];
            hsh1[i][k] = hsh1[i][k - 1] * ksm(p, 1ll << (k - 1)) + hsh1[i + (1ll << (k - 1))][k - 1];
        }
    }
    int l = 2, r = n, ans = 1;
    while(l <= r) {
        int mid = l + (r - l) / 2;
        if(check(mid)) {
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }
    cout << ans; el;
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号