看到这道题一开始想到的是后缀数组+二分+rmq
类似bzoj3172
问每个串i在合并后的串出现了多少次
等价于有多少个后缀j,使得LCP(i,j)>=length(s[i])
但是想想又不对,要求求的是有多少人被点到,每个人点到多少次
可能有多个后缀j满足条件但其实都是一个人的名字的一部分
好像二分搞不动,只能顺着名次依次找,理论上极其极端的数据是可以卡掉
但是实际却过了,,内疚啊……
UPD:太神了,这题有非暴力的做法,orz http://oi.nks.edu.cn/showmessage?message_id=4091
1 const inf=10010; 2 var s,sum,be,h,x,y,rank,sa:array[0..400010] of longint; 3 q,len,w:array[0..50010] of longint; 4 v:array[0..50010] of boolean; 5 tot,c,p,m,n,t,l,i,j:longint; 6 7 function min(a,b:longint):longint; 8 begin 9 if a>b then exit(b) else exit(a); 10 end; 11 12 procedure suffix(n:longint); 13 var m:longint; 14 begin 15 for i:=1 to t do 16 inc(sum[s[i]]); 17 m:=inf; 18 for i:=1 to m do 19 inc(sum[i],sum[i-1]); 20 for i:=n downto 1 do 21 begin 22 sa[sum[s[i]]]:=i; 23 dec(sum[s[i]]); 24 end; 25 p:=1; 26 rank[sa[1]]:=1; 27 for i:=2 to n do 28 begin 29 if (s[sa[i]]<>s[sa[i-1]]) then inc(p); 30 rank[sa[i]]:=p; 31 end; 32 m:=p; 33 j:=1; 34 while m<n do 35 begin 36 fillchar(sum,sizeof(sum),0); 37 y:=rank; 38 p:=0; 39 for i:=n-j+1 to n do 40 begin 41 inc(p); 42 x[p]:=i; 43 end; 44 for i:=1 to n do 45 if sa[i]>j then 46 begin 47 inc(p); 48 x[p]:=sa[i]-j; 49 end; 50 51 for i:=1 to n do 52 begin 53 rank[i]:=y[x[i]]; 54 inc(sum[rank[i]]); 55 end; 56 for i:=1 to m do 57 inc(sum[i],sum[i-1]); 58 for i:=n downto 1 do 59 begin 60 sa[sum[rank[i]]]:=x[i]; 61 dec(sum[rank[i]]); 62 end; 63 p:=1; 64 rank[sa[1]]:=1; 65 for i:=2 to n do 66 begin 67 if (y[sa[i]]<>y[sa[i-1]]) or (y[sa[i]+j]<>y[sa[i-1]+j]) then inc(p); 68 rank[sa[i]]:=p; 69 end; 70 m:=p; 71 j:=j shl 1; 72 end; 73 h[1]:=0; 74 p:=0; 75 for i:=1 to n do 76 begin 77 if rank[i]=1 then continue; 78 j:=sa[rank[i]-1]; 79 while s[i+p]=s[j+p] do inc(p); 80 h[rank[i]]:=p; 81 if p>0 then dec(p); 82 end; 83 end; 84 85 begin 86 readln(n,m); 87 for i:=1 to n do 88 begin 89 read(l); 90 for j:=1 to l do 91 begin 92 inc(t); 93 read(s[t]); 94 be[t]:=i; 95 end; 96 inc(t); 97 s[t]:=inf; //注意姓和名之间也要加分隔符,防止点名串一部分在姓,一部分在名的情况 98 read(l); 99 for j:=1 to l do 100 begin 101 inc(t); 102 read(s[t]); 103 be[t]:=i; 104 end; 105 inc(t); 106 s[t]:=inf; 107 end; 108 for i:=1 to m do 109 begin 110 read(len[i]); 111 w[i]:=t+1; 112 for j:=1 to len[i] do 113 begin 114 inc(t); 115 read(s[t]); 116 be[t]:=i+n; 117 end; 118 inc(t); 119 s[t]:=inf; 120 end; 121 suffix(t); 122 fillchar(sum,sizeof(sum),0); 123 tot:=0; 124 for i:=1 to m do 125 begin 126 for j:=1 to tot do //小小优化 127 v[q[j]]:=false; 128 tot:=0; 129 j:=rank[w[i]]; 130 l:=2147483647; 131 while j<=t do //找名次比点名串大的后缀 132 begin 133 inc(j); 134 c:=sa[j]; 135 l:=min(h[j],l); //height数组和LCP的关系 136 if l<len[i] then break 137 else begin 138 if (be[c]>=1) and (be[c]<=n) and not v[be[c]] then 139 begin 140 v[be[c]]:=true; //不能重复统计 141 inc(tot); 142 q[tot]:=be[c]; 143 inc(sum[be[c]]); 144 end; 145 end; 146 end; 147 j:=rank[w[i]]; 148 l:=h[j]; 149 while j>0 do //找名次比点名串小的后缀 150 begin 151 dec(j); 152 c:=sa[j]; 153 if l<len[i] then break 154 else begin 155 if (be[c]>=1) and (be[c]<=n) and not v[be[c]] then 156 begin 157 v[be[c]]:=true; 158 inc(tot); 159 q[tot]:=be[c]; 160 inc(sum[be[c]]); 161 end; 162 end; 163 l:=min(l,h[j]); 164 end; 165 writeln(tot); 166 end; 167 for i:=1 to n do 168 begin 169 write(sum[i]); 170 if i<>n then write(' '); 171 end; 172 writeln; 173 end.