• HDU6975 Forgiving Matching


    链接传送门


    这题比赛上做出来的人挺多的,于是我就一直在想SAM,结果这是一道用FFT解决的字符串模糊匹配的板子题。


    话说我很久以前还写过luogu P4173 残缺的字符串这道题,和比赛的题很像。

    首先能想到的是,对于长度为(m)的每个(S)的子串,我们只要统计其与(T)匹配的位置数(f_i),即可以得到该子串被认为匹配的最小的(k)值。

    对于求(f_i):先不考虑通配符的影响,因为字符集只有'0'~'9',所以我们可以每个字符单独考虑。拿'0'为例,将(S,T)所有是'0'的地方置为(1),其他为(0),则$$f(x)=sumlimits_{i=0}^{m-1}T(i)*S(x-m+1+i)$$

    按照套路,将(T)反转,即(T(i)=T'(m - i - 1)),则$$f(x)=sumlimits_{i=0}^{m-1}T'(m - i - 1) * S(x - m + i + 1)$$会发现等式右边括号内的和刚好是(x),变量代换得$$f(x) = sumlimits_{j=0}^{m - 1}T'(j) * S(x-j)$$这几乎就是卷积的形式了,只不过卷积的上界是(x),但到(x)和到(m-1)实际上是没有区别的,因此用对'0'~'9'依次用一遍FFT,就能求出(f(i))了。


    现在考虑通配字符,(S)的一个子串通过通配字符与(T)匹配的位置数=该子串的通配字符数+(T)中通配字符数-该子串和(T)中同一位置都是通配字符的数量。该子串的通配字符数用前缀和求得,该淄川和(T)中同一位置都是通配字符的数量同样同FFT可以求得。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<queue>
    #include<assert.h>
    #include<ctime>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 5.5e5 + 5;
    const db PI = acos(-1);
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) x = -x, putchar('-');
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    In void MYFILE()
    {
    #ifndef mrclr
    	freopen(".in", "r", stdin);
    	freopen(".out", "w", stdout);
    #endif
    }
    
    int n, m;
    char s[maxn], t[maxn];
    
    int len = 1, lim = 0, rev[maxn];
    struct Comp
    {
    	db x, y;
    	In Comp operator + (const Comp& oth)const {return (Comp){x + oth.x, y + oth.y};}
    	In Comp operator - (const Comp& oth)const {return (Comp){x - oth.x, y - oth.y};}
    	In Comp operator * (const Comp& oth)const {return (Comp){x * oth.x - y * oth.y, x * oth.y + y * oth.x};}
    	friend In void swap(Comp &a, Comp& b) {swap(a.x, b.x), swap(a.y, b.y);}
    }A[maxn], B[maxn];
    In void fft(Comp* a, int flg)
    {
    	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)
    	{
    		Comp omg = (Comp){cos(PI / i), sin(PI / i) * flg};
    		for(int j = 0; j < len; j += (i << 1))
    		{
    			Comp o = (Comp){1, 0};
    			for(int k = 0; k < i; ++k, o = o * omg)
    			{
    				Comp tp1 = a[k + j], tp2 = o * a[k + j + i];
    				a[k + j] = tp1 + tp2, a[k + j + i] = tp1 - tp2;
    			}
    		}
    	}
    }
    
    int f[maxn], g[maxn], f1[maxn], ans[maxn];	
    //f[i]:即题解中的f[i] 
    //g[i]:以i结尾的子串和T同一位置都是通配字符的数量 
    //f1[i]:S中通配字符数量的前缀和 
    In void solve()
    {
    	reverse(t, t + m);
    	len = 1, lim = 0;
    	while(len <= n + m) len <<= 1, ++lim;
    	fill(f, f + n, 0);
    	for(int i = 0; i < len; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lim - 1));
    	for(int x = 0; x <= 10; ++x)
    	{
    		for(int i = 0; i < len; ++i) A[i] = B[i] = (Comp){0, 0};
    		char c = x == 10 ? '*' : x + '0';
    		for(int i = 0; i < n; ++i) if(s[i] == c) A[i].x = 1;
    		for(int i = 0; i < m; ++i) if(t[i] == c) B[i].x = 1;
    		fft(A, 1), fft(B, 1);
    		for(int i = 0; i < len; ++i) A[i] = A[i] * B[i];
    		fft(A, -1);
    		for(int i = 0; i < n; ++i) 
    			if(x == 10) g[i] = A[i].x / len + 0.5;
    			else f[i] += A[i].x / len + 0.5;
    	}
    	int f2 = 0;
    	for(int i = 0; i < n; ++i) f1[i] = (i ? f1[i - 1] : 0) + (s[i] == '*');
    	for(int i = 0; i < m; ++i) f2 += (t[i] == '*');
    	fill(ans, ans + m + 1, 0);
    	for(int i = m - 1; i < n; ++i) ans[m - (f[i] + f1[i] - (i < m ? 0 : f1[i - m]) + f2 - g[i])]++;
    	for(int i = 0; i <= m; ++i)
    	{
    		if(i) ans[i] += ans[i - 1];
    		write(ans[i]), enter;
    	}
    }
    
    int main()
    {
    	int T = read();
    	while(T--)
    	{
    		n = read(), m = read();
    		scanf("%s%s", s, t);
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    优化linux,安装node环境
    腾讯云主机的公网无法访问,putty和FileZilla连接不上
    angularjs1.x的directive中的link参数element见解
    springboot自动装配原理
    redis哨兵部署
    HashMap笔记
    多线程与高并发
    java面试必会知识点
    maven仓库
    java任务调度简单示例
  • 原文地址:https://www.cnblogs.com/mrclr/p/15071763.html
Copyright © 2020-2023  润新知