首页 > 试题广场 >

Just A String

[编程题]Just A String
  • 热度指数:5 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
何老师手中有一个字符串S,他发现这个字符串有一个神奇的性质,取出一个长为i的前缀(就是由S的前i个字符顺序构成的字符串)prei和一个长为j的后缀(就是由S的后j个字符顺序构成的字符串)sufj之后,总是存在三个字符串A,B,C(可能为空)使得prei=A+B,sufj=B+C, 虽然这听起来像是一句废话。 
显然三元组A,B,C不总是唯一的,何老师从所有可能的三元组中找到B最长的,很容易知道这样的三元组是唯一的,并且认为prei和sufj的契合度就是f(i,j)=|A||B|2|C|,现在你需要帮何老师算出所有f(i,j)(0 ≤ i,j ≤ n)的异或和。 
这里|X|表示字符串X的长度,X+Y表示将两个字符串X和Y顺序拼接起来后得到的新字符串。

输入描述:
第一行是一个正整数T(≤ 500),表示测试数据的组数, 每组测试数据,包含一个仅由小写字母构成的非空字符串S(|S| ≤ 2000), 保证满足|S|>200的数据不超过5组。


输出描述:
对于每组测试数据,输出所有f(i,j)(0 ≤ i,j ≤ n)的异或和。
示例1

输入

1
abcab

输出

13
  #include<bits/stdc++.h>     
  using namespace std; 
  typedef long long  ll;
  typedef pair<ll,ll> pll; 
  #define pb(x) push_back(x)     
  typedef unsigned long long  ull;     
  #define mem(A, X) memset(A, X, sizeof A)
  #define ford(i,l,u) for(ll (i)=(ll)(l);(i)>=(ll)(u);--(i))
  #define foreach(e,x) for(__typeof(x.begin()) e=x.begin();e!=x.end();++e)
  #define fori(i,l,u) for(ll (i)=(ll)(l);(i)<=(ll)(u);++(i))   
  typedef pair<int,int> pii;
  #define mp  make_pair
  #define sec second
  #define fir first 

  const ll mod=1e9+7;
  const ll Maxn=2e3+10;


   ll Next[Maxn];

   void GetNext(char *p,ll m)
   {
       Next[0]=Next[1]=0;

       fori(i,1,m-1)
       {
        ll j=Next[i];
        while(j&&p[i]!=p[j]) j=Next[j];
        Next[i+1]= p[i]==p[j] ? j+1:0;
       }
   }

   ll Common[Maxn];
   void Find(char *s,char *p,ll m)
   {
    GetNext(p,m);
    ll n=strlen(s);
    //fori (i,0,m-1) cout<<Next[i]<<" ";
    //cout<<endl;
    ll j=0;
    //fori (i,0,m-1) printf("%d ",Next[i]);
    //printf("\n j: ");
    fori (i,0,n-1) {
      while(j&&s[i]!=p[j]) j=Next[j]; 
      if(s[i]==p[j]) j++;

      //printf("__%d  i-j+1: %d ",j,i-j+1);
      if(j==0) Common[i]=0;
        else Common[i]=j;
      if(j==m) j=Next[j];// 排除越界访问。
    } 
    //puts("");
   }


  int main()
  {
       std::ios::sync_with_stdio(false);
       //freopen("in.txt","r",stdin);
       int T;
       while(scanf("%d",&T) != EOF)
       {
        char s[Maxn];
        scanf("%s",s); 
        ll n = strlen(s);
        ll ret=0;
        fori (j,1,n) {
          fori(di,0,n-1) Common[di]=0;
          Find(s, s + (n-j), j);   
          //printf("%s___\n",s + (n - j ));
         // fori (di, 0, n-1) printf("%d ",Common[di]);
         // puts("");
          //puts("");
          fori (id, 0, n-1) {
            ll A,B,C,i; 
            B = Common[id];
            i=id+1;
            A = i - B;
            C = j - B;
            ll temp = A * B * B * C;
            //cout<<"A B C:"<<A<<" "<<B<<" "<<C<<"  temp: "<<temp<<endl;
            //cout<<temp<<endl;
            ret= ret ^ temp;
          }
        }
        printf("%lld\n",ret);
       }   
    return 0;
  }
分析考虑每个f(i,j)的含义,其含义与kmp算法中next数组几乎一致,只是细节上一个下标还有一些对应关系的差别。  之后就是考虑快速得到所有的f值,
               枚举每一个后缀sufj,对sufj和完整的文本串进行匹配。由于sufj为后缀,即可确定f()中固定的j,而对于sufj和完整文本串匹配的时候,某个Nexti值就确定出来了后缀j与前缀i(后缀j指文本串的下标j,j+1,... n-1 构成的后缀,同理prei)的魅力值。回顾某个确定的后缀j会发现,j是和所有的i=0 1 2 3 ... n-1 一一对应的,所以只需要枚举所有的后缀均和文本串进行一次kmp即可。
发表于 2017-09-13 09:57:16 回复(0)