• [洛谷P4173] 残缺的字符串


    前言

    经典问题。

    题目

    洛谷

    DarkBZOJ

    讲解

    匹配两个字符串 \(S,T\)\(S\) 为模板串,考虑将字符转换为数字,为了方便,这里只考虑小写字母,\(a\rightarrow 1\)

    然后我们定义距离函数 \(dis(f,g)=\sum |f_i-g_i|\),绝对值不方便,所以我们平方。

    \(dis(f,g)=\sum (f_i-g_i)^2\),这玩意不能快速求,考虑翻转 \(f\),距离函数变为 \(dis(f,g)=\sum (f_{i-j}-g_j)^2\) ,显然可以用多项式乘法做,最后得到系数为 0 的地方就是成功匹配的末尾位置。

    这道题有通配符 * ,考虑 \(*\rightarrow 0\),更新距离函数:\(dis(f,g)=\sum (f_{i-j}-g_j)^2f_{i-j}g_j\)

    暴力展开后有三项,分别做多项式乘法即可。这道题用FFT就行了,NTT会被卡。

    注意 eps 不能设小了。

    代码

    //12252024832524
    #include <bits/stdc++.h>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 1 << 20 | 5;
    const double PI = acos(-1);
    const double eps = 0.5;
    int lena,lenb,cnt,na[MAXN],nb[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1; char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    struct cp
    {
    	double x,y;
    	cp(){}
    	cp(double x1,double y1){
    		x = x1;
    		y = y1;
    	}
    	cp operator + (const cp &A)const{return cp(x+A.x,y+A.y);}
    	cp operator - (const cp &A)const{return cp(x-A.x,y-A.y);}
    	cp operator * (const cp &A)const{return cp(x*A.x-y*A.y,x*A.y+y*A.x);}
    }a[MAXN],b[MAXN],c[MAXN],I;
    
    int rev[MAXN],len;
    void pre(int L)
    {
    	int l = -1; len = 1;
    	while(len <= L) len <<= 1,l++;
    	for(int i = 0;i <= len;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l);
    }
    void FFT(cp *a,int f)
    {
    	for(int i = 0;i < len;++ i) if(i < rev[i]) swap(a[i],a[rev[i]]);
    	for(int i = 1;i < len;i <<= 1)
    		for(int j = 0,p = i << 1;j < len;j += p)
    		{
    			cp mi = cp(1,0),w = cp(cos(PI/i),f*sin(PI/i));
    			for(int k = 0;k < i;++ k,mi = mi * w)
    			{
    				cp X = a[j+k],Y = a[i+j+k];
    				a[j+k] = X + mi * Y;
    				a[i+j+k] = X - mi * Y;
    			}
    		}
    	if(f == -1) for(int i = 0;i < len;++ i) a[i].x /= len;
    }
    
    char gc()
    {
    	char c = getchar();
    	while((c > 'z' || c < 'a') && c != '*') c = getchar();
    	return c;
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	lena = Read(); lenb = Read();
    	for(int i = lena-1;i >= 0;-- i) na[i] = Max(gc()-'a'+1,0);
    	for(int i = 0;i < lenb;++ i) nb[i] = Max(gc()-'a'+1,0);
    	pre(lena+lenb);
    	
    	for(int i = 0;i < len;++ i) if(i < lena) a[i] = cp{na[i] * na[i] * na[i],0}; else a[i] = I;
    	for(int i = 0;i < len;++ i) if(i < lenb) b[i] = cp{nb[i],0}; else b[i] = I;
    	FFT(a,1); FFT(b,1);
    	for(int i = 0;i < len;++ i) c[i] = c[i] + a[i] * b[i];
    	
    	for(int i = 0;i < len;++ i) if(i < lena) a[i] = cp{na[i] * na[i],0}; else a[i] = I;
    	for(int i = 0;i < len;++ i) if(i < lenb) b[i] = cp{nb[i] * nb[i],0}; else b[i] = I;
    	FFT(a,1); FFT(b,1);
    	for(int i = 0;i < len;++ i) c[i] = c[i] + a[i] * b[i] * cp{-2,0};
    	
    	for(int i = 0;i < len;++ i) if(i < lena) a[i] = cp{na[i],0}; else a[i] = I;
    	for(int i = 0;i < len;++ i) if(i < lenb) b[i] = cp{nb[i] * nb[i] * nb[i],0}; else b[i] = I;
    	FFT(a,1); FFT(b,1);
    	for(int i = 0;i < len;++ i) c[i] = c[i] + a[i] * b[i];
    	
    	FFT(c,-1);
    	for(int i = lena-1;i < lenb;++ i) if(Abs(c[i].x) < eps) ++cnt;
    	Put(cnt,'\n');
    	for(int i = lena-1;i < lenb;++ i) if(Abs(c[i].x) < eps) Put(i-lena+2,' ');
    	return 0;
    }
    
  • 相关阅读:
    CSS三大特性
    CSS选择器
    CSS(1)
    多媒体标签
    快捷键
    H5注意点(2)
    H5注意点(1)
    List详解
    求最大子串和以及其中一个子串(java)
    隧道项目
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15772237.html
Copyright © 2020-2023  润新知