数一数(KMP+思维)
题目链接:https://ac.nowcoder.com/acm/skill/detail/acm/1267
题目描述
设s,t为两个字符串,定义f(s,t) = t的子串中,与s相等的串的个数。如f("ac","acacac")=3, f("bab","babab")=2。现在给出n个字符串,第i个字符串为si。你需要对∀1≤i≤n∀1≤i≤n,求出∏nj=1f(si,sj)∏j=1nf(si,sj),由于答案很大,你只需要输出对 998244353取模后的结果。
输入描述:
第一行一个整数n。
接下来n行每行一个仅由英文字母构成的非空字符串,第i个字符串代表si。
输出描述:
共n行,第i行输出∏nj=1f(si,sj)∏j=1nf(si,sj)对 998244353取模的结果。
具体思路:
首先瞎分析一波复杂度,1e6个字符串,然后总长度不超过2e6,要不就是n大,然后每个字符串的长度很小,这个时候会有重复。
要不就是n小,但是每个字符串的长度不算小(xjbc)。
然后我们在继续往下想,如果有一个字符的匹配另一个字符的返回的合法的个数为0的话,那么0乘任何数都为0.
再继续往下想,如果有多个字符串重复的时候,我们计算完当前的字符串的时候,对于重复的直接快速幂就可以了。
再继续往下想,如果在两个字符串不完全相等的前提下,如果str1是str2的字符串,那么str2就不会是str1的子串,所以我们当计算str1的子串的时候,str2的答案必为0。
AC代码:
(快速幂打错了调了一上午。。。)
1 #include<bits/stdc++.h>
2 using namespace std;
3 # define inf 0x3f3f3f3f
4 # define ll long long
5 const ll mod = 998244353;
6 const int maxn = 1e6+100;
7 map<string,ll>vis;
8 map<string,ll>tot;
9 map<string,ll>pre;
10 string str[maxn];
11 int id[maxn];
12 ll ans[maxn];
13 int nex[maxn];
14 ll qsm(ll t1,ll t2){
15 ll ans=1ll;
16 t1%=mod;
17 while(t2){
18 if(t2&1)ans=ans*t1%mod;
19 t2>>=1;
20 t1=t1*t1%mod;
21 }
22 return ans;
23 }
24 void getnex(string str2)
25 {
26 int len=str2.size();
27 int i=0,j=-1;
28 nex[0]=-1;
29 while(i<len)
30 {
31 if(j==-1||str2[i]==str2[j])
32 {
33 i++;
34 j++;
35 nex[i]=j;
36 }
37 else
38 j=nex[j];
39 }
40 }
41 ll kmp(string str1,string str2)
42 {
43 getnex(str2);
44 int len1=str1.size();
45 int len2=str2.size();
46 int i=0,j=0;
47 ll sum=0;
48 while(i<len1&&j<len2)
49 {
50 if(j==-1||str1[i]==str2[j])
51 {
52 i++;
53 j++;
54 }
55 else
56 j=nex[j];
57 if(j==len2)
58 {
59 sum+=1ll;
60 j=nex[j];
61 }
62 }
63 return sum;
64 }
65 int main()
66 {
67 // string str1,str2;
68 // str1="babab";
69 // str2="bab";
70 // cout<<kmp(str1,str2)<<endl;
71 int n;
72 scanf("%d",&n);
73 for(int i=1; i<=n; i++)
74 {
75 cin>>str[i];
76 vis[str[i]]++;
77 if(vis[str[i]]==1)
78 {
79 pre[str[i]]=i;
80 id[i]=0;
81 }
82 else
83 {
84 id[i]=pre[str[i]];
85 }
86 }
87 for(int i=1; i<=n; i++)
88 {
89 ans[i]=1ll;
90 }
91 for(int i=1; i<=n; i++)
92 {
93 if(id[i]||ans[i]==0)
94 continue;
95 for(int j=1; j<=n; j++)
96 {
97 if(id[j]||i==j)
98 continue;
99 ll tmp=kmp(str[j],str[i]);
100 if(tmp==0)
101 {
102 ans[i]=0;
103 break;
104 }
105 ans[j]=0;
106 ans[i]=ans[i]*qsm(tmp,vis[str[j]])%mod;
107 ans[i]%=mod;
108 }
109 }
110 for(int i=1; i<=n; i++)
111 {
112 printf("%lld\n",ans[id[i]==0?i:id[i]]);
113 }
114 return 0;
115 }
再想一下,如果是把相乘改成相加又应该怎么做?