【HNOI2014】抄卡组___思维+Kmp+hash

时间:2021-01-24 16:46:37

题目大意:

【HNOI2014】抄卡组___思维+Kmp+hash
【HNOI2014】抄卡组___思维+Kmp+hash

题解:

这题其实认真读题可以发现:
对于任意匹配的2个字符串x,y,无非就3种情况:
①x,y都不含 * ,这里可以直接用hash判断所有的这种情况的x是否相等,因为不含*,所以x必定等于y,否则必定不匹配
②x,y都含 * ,此时我们发现当x的第一个 * 的前缀,设为xy1,与y的第一个 * 的前缀,设为xy2
必定短的一个是长的一个的前缀,否则无法匹配
对于他们的后缀,设yx1为x中[最后一个 * 位置,x长度],yx2同理
则必定短的一个是长的一个的后缀,否则也无法匹配
这一判断可以用枚举实现,当然排序可以更优,尽管我没加
注意,对于一种情况:
1qaz2wsx3edc4rfv5tgb6yhn7uj*
*qaz2wsx4edc4rfv5tgb6yhn7ujm
这种情况是无法匹配的,而我一开始以为能够匹配,ε=(´ο`*)))唉

③x,y任意一个中含 * ,另外一个不含 *
假设含 * 的是x,那么我们可以将x的 * 全部去掉,
然后分开成一个个子串x1,x2,x3……
我们将x1,x2,x3……去跟y匹配,只要y满足x1,x2,x3能够依次出现即能够匹配,否则不匹配
这里能够用Kmp实现,不过要注意:
y并不用去枚举,因为对于任意一组卡组,它只可能存在一组y,在读入的时候记录即可,
而如果全部都带 * 则不存在这样一个不含 * 的 y,此时跳到②去实现

代码:

const
     modn=10000007;
var
     a:array [0..1000001] of ansistring;
     b,d:array [0..1000001] of boolean;
     next:array [0..20000001] of longint;
     c:array [0..1000001,1..2] of longint;
     maxl,minr,i,j,k,l,ii,jj,kk,n,t,m:longint;
     op,s:ansistring;
     check:boolean;

function insert():longint;
var
     j:longint;
begin
     insert:=0;
     for j:=1 to length(a[i]) do
     begin
         if (c[i,1]=0) and (a[i][j]='*') then c[i,1]:=j;
         if a[i][j]='*' then c[i,2]:=j;
         insert:=(insert+ord(a[i][j])*j) mod modn;
     end;
end;

begin
    assign(input,'hs.in'); reset(input);
    assign(output,'hs.out');rewrite(output);
     readln(t);
     while t>=1 do
      begin
           k:=0;
           j:=0;
           l:=0;
           maxl:=0;
           minr:=0;
           readln(n);
           check:=true;
           for i:=1 to n do
             begin
                    readln(a[i]);
                    c[i,1]:=0;
                    c[i,2]:=0;
                    j:=insert();
                    b[i]:=true;
                    d[i]:=true;
                    if c[i,1]=0 then
                      begin
                          if (k<>0) and (j<>k) then check:=false;
                          k:=j;
                          b[i]:=false;
                          d[i]:=false;
                          l:=i;
                      end
                      else begin
                                if c[i,1]>maxl then maxl:=c[i,1];
                                if length(a[i])-c[i,2]>minr
                                   then minr:=length(a[i])-c[i,2];
                           end;
             end;

           if check then
           begin
              for i:=1 to maxl do
                if check then
                   begin
                        for j:=1 to n do
                          if b[j] then
                             if a[j][i]='*' then b[j]:=false;
                        for j:=1 to n do
                           if (b[j]) then break;
                        if b[j] then
                            for k:=1 to n do
                               if b[k] then
                                  if a[j][i]<>a[k][i] then check:=false;
                   end;
              for i:=1 to minr do
                if check then
                    begin
                        for j:=1 to n do
                          if  d[j] then
                             if a[j][length(a[j])-i+1]='*' then d[j]:=false;
                        for j:=1 to n do
                          if (d[j]) then break;
                        if d[j] then
                           for k:=1 to n do
                              if d[k] then
                                 if a[k][length(a[k])-i+1]<>a[j][length(a[j])-i+1]
                                    then check:=false;
                    end;
           end;
          if check then
              if l<>0 then
               begin
                  for kk:=1 to n do
                    if check then
                      if c[kk,1]<>0 then
                         begin
                              op:='';
                              k:=1;
                              if a[kk,1]='*' then delete(a[kk],1,1);
                             if a[kk][length(a[kk])]<>'*'
                                 then a[kk]:=a[kk]+'*';
                              for i:=1 to length(a[kk]) do
                                if check then
                                if a[kk][i]='*' then
                                    begin
                                         next[1]:=0;
                                         jj:=0;

                                         for ii:=2 to length(op) do
                                           begin
                                                while (jj>0) and (op[jj+1]<>op[ii]) do jj:=next[jj];
                                                if op[jj+1]=op[ii] then inc(jj);
                                                next[ii]:=jj;
                                           end;

                                         jj:=0;
                                         for j:=k to length(a[l]) do
                                           begin
                                                while (jj>0) and (op[jj+1]<>a[l][j]) do jj:=next[jj];
                                                if jj+1<=length(op) then
                                                   if a[l][j]=op[jj+1] then inc(jj);
                                                if jj=length(op) then break;
                                           end;
                                         if jj=length(op) then k:=j+1
                                                          else check:=false;
                                         op:='';

                                    end
                                 else op:=op+a[kk][i];
                         end;
               end;
           if check then writeln('Y')
                    else writeln('N');
           dec(t);
      end;
     close(input); close(output);
end.