Om Nom and Necklace 重温

12月就做过了,突然发现没发过题解

第一种方法是二分+哈希

首先 ∣ A ∣ + ∣ B ∣ ∈ [ 1 , ⌈ n k ⌉ ] |A|+|B| \in [1,\left \lceil \frac{n}{k} \right \rceil] A+B[1,kn],这个很显然

i = ∣ A ∣ + ∣ B ∣ i=|A|+|B| i=A+B

那么可以二分 k ∗ i − B k*i-B kiB,用差分维护

这个很显然就不说了

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std ;

#define rep(i, a, b) for (int (i) = (a); (i) <= (b); (i)++)
#define Rep(i, a, b) for (int (i) = (a) - 1; (i) < (b); (i)++)
#define REP(i, a, b) for (int (i) = (a); (i) >= (b); (i)--)
#define clr(a) memset(a, 0, sizeof(a))
#define Sort(a, len, cmp) sort(a + 1, a + len + 1, cmp)
#define ass(a, sum) memset(a, sum, sizeof(a))

#define ls ((rt) << 1)
#define rs ((rt) << 1 | 1)
#define lowbit(x) (x & -x)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define endl '\n'
#define ENDL cout << endl
#define SZ(x) ((int)x.size())

typedef long long ll ;
typedef unsigned long long ull ;
typedef vector <int> vi ;
typedef pair <int, int> pii ;
typedef pair <ll, ll> pll ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
typedef map <ll, ll> mll ;

const int N = 1000010 ;
const double eps = 1e-8 ;
const int iinf = INT_MAX ;
const ll linf = 2e18 ;
const double dinf = 1e30 ;
const int MOD = 1000000007 ;
const int x = 25625462 ;
const int y = 46835134 ;

inline int read(){
    int X = 0, w = 0 ;
    char ch = 0 ;
    while (!isdigit(ch)) { w |= ch == '-' ; ch = getchar() ; }
    while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar() ;
    return w ? - X : X ;
}

void write(int x){
     if (x < 0) putchar('-'), x = - x ;
     if (x > 9) write(x / 10) ;
     putchar(x % 10 + '0') ;
}

void print(int x) {
    cout << x << endl ;
    exit(0) ;
}

void PRINT(string x) {
    cout << x << endl ;
    exit(0) ;
}

void douout(double x){
     printf("%lf\n", x + 0.0000000001) ;
}

string ans ;
char s[N] ;
ll hashx[N], hashy[N], powx[N], powy[N], a[N] ;
int n, k ;

bool check(int p, int q, int d)  { // yes, double hash
    ll hashp = hashx[p + d] - hashx[p] ;
    if (hashp < 0) hashp += MOD ;
    hashp = hashp * powx[q - p] % MOD ;
    ll hashq = hashx[q + d] - hashx[q] ;
    if (hashq < 0) hashq += MOD ;
    if (hashp != hashq) return false ;
    
    hashp = hashy[p + d] - hashy[p] ;
    if (hashp < 0) hashp += MOD ;
    hashp = hashp * powy[q - p] % MOD ;
    hashq = hashy[q + d] - hashy[q] ;
    if (hashq < 0) hashq += MOD ;
    if (hashp != hashq) return false ;
    return true ;
}

signed main(){
    scanf("%d%d", &n, &k) ;
    scanf("%s", s) ;
    powx[0] = 1 ;
    for (int i = 0; i < n; i++) powx[i + 1] = powx[i] * x % MOD ;
    for (int i = 0; i < n; i++) hashx[i + 1] = (hashx[i] + (ll) (s[i] - 'a') * powx[i]) % MOD ;
    powy[0] = 1 ;
    for (int i = 0; i < n; i++) powy[i + 1] = powy[i] * y % MOD ;
    for (int i = 0; i < n; i++) hashy[i + 1] = (hashy[i] + (ll) (s[i] - 'a') * powy[i]) % MOD ;
    for (int i = 0; i <= n / k; i++) { // 下一个a的起点
        int low = 0, high = n + 1 ;
        while (low + 1 < high) {
            int mid = (low + high) >> 1 ;
            if (i + mid <= n && check(0, i, mid)) low = mid ;
            else high = mid ;
        }
        int L = k * i ; int R = min(L + i, i + low) ;
        if (L <= R) {
            a[L]++ ;
            a[R + 1]-- ;
        }
    }
    for (int i = 0; i < n; i++) a[i + 1] += a[i] ;
    for (int i = 0; i < n; i++) ans += ((a[i + 1] > 0) ? '1' : '0') ;
    cout << ans << endl ;
}

/*
写代码时请注意:
    1.是否要开Long Long?数组边界处理好了么?
    2.实数精度有没有处理?
    3.特殊情况处理好了么?
    4.做一些总比不做好。
思考提醒:
    1.最大值和最小值问题可不可以用二分答案?
    2.有没有贪心策略?否则能不能dp?
*/

然后根据 R a d i o   T r a n s i m i s s i o n \mathsf{Radio~Transimission} Radio Transimission,我们知道一个串的最小循环节 = n − n x t [ n ] =n-nxt[n] =nnxt[n]

这个题目的 ∣ A ∣ + ∣ B ∣ |A|+|B| A+B 其实就相对应着循环节(但不一定是最小的)

设最小循环节为 m i n p d \mathsf{minpd} minpd

那么我们显然知道 ∣ A ∣ + ∣ B ∣ = m i n p d ∗ q |A|+|B|=\mathsf{minpd}*q A+B=minpdq

加入枚举到长度 i i i

可以这么推导:

i = k ∗ ( ∣ A ∣ + ∣ B ∣ ) + ∣ A ∣ i=k*(|A|+|B|)+|A|\\ i=k(A+B)+A
∣ A ∣ |A| A 肯定小于等于 ∣ A ∣ + ∣ B ∣ |A|+|B| A+B,所以原式等价于
k ∗ q ∗ m i n p d + [ 0 , q ∗ m i n p d ] = [ k ∗ q ∗ m i n p d , ( k + 1 ) ∗ q ∗ m i n p d ] k*q*\mathsf{minpd}+[0,q*\mathsf{minpd}]\\ =[k*q*\mathsf{minpd},(k+1)*q*\mathsf{minpd}] kqminpd+[0,qminpd]=[kqminpd,(k+1)qminpd]

答案显然是要求是否存在一个正整数 q q q

那么

q = [ i ( k + 1 ) m i n p d , i k m i n p d ] q=[\frac{i}{(k+1)\mathsf{minpd}},\frac{i}{k\mathsf{minpd}}] q=[(k+1)minpdi,kminpdi]

于是就搞定了

时间复杂度 O ( n ) O(n) O(n),很优秀

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std ;
//#define int long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)
#define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)
#define clr(a) memset(a, 0, sizeof(a))
#define ass(a, sum) memset(a, sum, sizeof(a))
#define lowbit(x) (x & -x)
#define all(x) x.begin(), x.end()
#define ub upper_bound
#define lb lower_bound
#define pq priority_queue
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define iv inline void
#define enter cout << endl
#define siz(x) ((int)x.size())
#define file(s) freopen(s".in", "r", stdin), freopen(s."out", "w", stdout)
typedef long long ll ;
typedef unsigned long long ull ;
typedef pair <int, int> pii ;
typedef vector <int> vi ;
typedef vector <pii> vii ;
typedef queue <int> qi ;
typedef queue <pii> qii ;
typedef set <int> si ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
const int N = 1000010 ;
const int INF = 0x3f3f3f3f ;
const int iinf = 1 << 30 ;
const ll linf = 2e18 ;
const int MOD = 1000000007 ;
const double eps = 1e-7 ;
void print(int x) { cout << x << endl ; exit(0) ; }
void PRINT(string x) { cout << x << endl ; exit(0) ; }
void douout(double x){ printf("%lf\n", x + 0.0000000001) ; }

int nxt[N] ;
char s[N] ;
int n, k ;

void init() {
	nxt[1] = 0 ;
	for (int i = 2, j = 0; i <= n; i++) {
		while (j && s[j + 1] != s[i]) j = nxt[j] ;
		if (s[j + 1] == s[i]) j ++ ;
		nxt[i] = j ;
	}
}

int ok(int x) {
	int len = x - nxt[x] ;
	int l = ((x - 1) / len) / (k + 1) + 1, r = (x / len) / k ;
	return l <= r ;
}


signed main(){
	scanf("%d%d", &n, &k) ; scanf("%s", s + 1) ;
	init() ;
	rep(i, 1, n) cout << ok(i) ;
	enter ;
	return 0 ;
}

/*
写代码时请注意:
	1.ll?数组大小,边界?数据范围?
	2.精度?
	3.特判?
	4.至少做一些
思考提醒:
	1.最大值最小->二分?
	2.可以贪心么?不行dp可以么
	3.可以优化么
	4.维护区间用什么数据结构?
	5.统计方案是用dp?模了么?
	6.逆向思维?
*/


还有倍增 + k m p +kmp +kmp 的,多加一个 l o g log log,不怎么优秀,在这就不说了

你可能感兴趣的:(———字符串———,倍增,字符串哈希,KMP)