生活随笔
收集整理的這篇文章主要介紹了
HDU - 6975 Forgiving Matching FFT匹配字符串
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
傳送門
文章目錄
題意:
給你兩個串a,ba,ba,b長度分別為n,mn,mn,m,你需要輸出m+1m+1m+1個數,第iii個數表示當允許有i?1i-1i?1個數可以不匹配時aaa中長度為mmm的子串與bbb匹配的數量,匹配的意思就是可以有i?1i-1i?1個位置不同,其他位置相同。
n≤2e5,a∈(0,1,...,9,?)n\le2e5,a\in {(0,1,...,9,*)}n≤2e5,a∈(0,1,...,9,?),其中?*?代表通配符,即與任何其他字符相同。
思路:
不多bb,直接上思路:
由于字符集很小,考慮每個字符對答案的影響。
考慮ai=bja_i=b_jai?=bj?,那么在aaa中以i+m?ji+m-ji+m?j的位置為結尾的長度為mmm的子串中,他的貢獻是111,由于其滿足i+m?j=ki+m-j=ki+m?j=k,所以容易想到用FFTFFTFFT快速計算。為了處理m?jm-jm?j,考慮將其reversereversereverse一下,這樣其實就變成了i+j=ki+j=ki+j=k,直接FFTFFTFFT卷一下就可以啦。
假設我們已經卷出來fff數組了,該怎么用它呢?
先考慮沒有通配符的情況,我們可以發現m?f[i]m-f[i]m?f[i]就是他不能匹配的數量,所以給ans[m?f[i]]++ans[m-f[i]]++ans[m?f[i]]++,讓后求一個前綴和即可。
對于通配符,我們考慮容斥的來求,即acnt+bcnt?fcnta_{cnt}+b_{cnt}-f_{cnt}acnt?+bcnt??fcnt?,fcntf_{cnt}fcnt?即對?*?卷一下得到的答案。
有一個小細節可以優化,就是我們發現最終卷完只有指數為nnn以內的項有用,所以在卷的時候卷到nnn即可。
#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std
;
typedef long long LL
;
typedef unsigned long long ULL
;
typedef pair
<int,int> PII
;const int N
=1000010,mod
=1e9+7,INF
=0x3f3f3f3f;
const double eps
=1e-6,PI
=acos(-1);int n
,m
;
int rev
[N
];
int bit
,limit
;
int f
[N
],pre
[N
],ans
[N
];
char s1
[N
],s2
[N
];
double p1
[N
],p2
[N
];struct Complex {double x
,y
;Complex
operator + (const Complex
& t
) const { return {x
+t
.x
,y
+t
.y
}; }Complex
operator - (const Complex
& t
) const { return {x
-t
.x
,y
-t
.y
}; }Complex
operator * (const Complex
& t
) const { return {x
*t
.x
-y
*t
.y
,x
*t
.y
+y
*t
.x
}; }
}a
[N
],b
[N
];void fft(Complex a
[],int inv
) {for(int i
=0;i
<limit
;i
++) if(i
<rev
[i
]) swap(a
[i
],a
[rev
[i
]]);for(int mid
=1;mid
<limit
;mid
<<=1) {Complex w1
=Complex({p1
[mid
],inv
*p2
[mid
]});for(int i
=0;i
<limit
;i
+=mid
*2) {Complex wk
=Complex({1,0});for(int j
=0;j
<mid
;j
++,wk
=wk
*w1
) {Complex x
=a
[i
+j
],y
=wk
*a
[i
+j
+mid
];a
[i
+j
]=x
+y
; a
[i
+j
+mid
]=x
-y
;}}}
}int main()
{
int _
; scanf("%d",&_
);while(_
--) {int cnt
=0;scanf("%d%d%s%s",&n
,&m
,s1
,s2
);pre
[0]=0; ans
[m
]=0; ans
[m
+1]=0;for(int i
=0;i
<n
;i
++) {if(i
>0) pre
[i
]=pre
[i
-1];pre
[i
]+=s1
[i
]=='*';if(i
<m
) cnt
+=s2
[i
]=='*';ans
[i
]=0; f
[i
]=0;}reverse(s2
,s2
+m
);bit
=0;while((1<<bit
)<=n
) bit
++;limit
=1<<bit
;for(int i
=0;i
<limit
;i
++) rev
[i
]=(rev
[i
>>1]>>1)|((i
&1)<<(bit
-1));for(int mid
=1;mid
<limit
;mid
<<=1) p1
[mid
]=cos(PI
/mid
),p2
[mid
]=sin(PI
/mid
);for(int op
=0;op
<=10;op
++) {char now
=op
+'0';if(op
==10) now
='*';for(int i
=0;i
<limit
;i
++) a
[i
]=b
[i
]={0,0};for(int i
=0;i
<n
;i
++) a
[i
]={s1
[i
]==now
? 1.0:0,0};for(int i
=0;i
<m
;i
++) b
[i
]={s2
[i
]==now
? 1.0:0,0};fft(a
,1); fft(b
,1);for(int i
=0;i
<limit
;i
++) a
[i
]=a
[i
]*b
[i
];fft(a
,-1);for(int i
=m
-1;i
<n
;i
++) {int val
=(int)(a
[i
].x
/limit
+0.5);if(op
<10) f
[i
]+=val
;else f
[i
]-=val
;}}for(int i
=m
-1;i
<n
;i
++) {int all
=pre
[i
];if(i
>m
-1) all
-=pre
[i
-m
];all
+=cnt
+f
[i
];ans
[m
-all
]++;}for(int i
=1;i
<=m
;i
++) ans
[i
]+=ans
[i
-1];for(int i
=0;i
<=m
;i
++) printf("%d\n",ans
[i
]);}return 0;
}
總結
以上是生活随笔為你收集整理的HDU - 6975 Forgiving Matching FFT匹配字符串的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。