zoukankan      html  css  js  c++  java
  • Codeforces 177G2 Fibonacci Strings KMP 矩阵

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF117G2.html

    题目传送门 - CF177G2

    题意

      定义斐波那契字符串如下:

      $s_1="a"$

      $s_2="b"$

      $s_i=s_{i-1}+s_{i-2} (igeq 3)$

      给定 $k,m$,以及对应的 $m$ 组询问。

      每组询问一个字符串 $x$ ,问 $x$ 在 $s_k$ 中出现了多少次。

      $kleq 10^{18},mleq 10^4,|x|leq 10^5$

    题解

      看到 $k$ 如此大,首先要想到矩阵快速幂。

      但这个想法暂时还没什么用。

      让我们来观察一下字符串的性质。

      下面我们分别左对齐和右对齐来看一看斐波那契串。

    $$egin{eqnarray*}a\b\ba\bab\babba\babbabab\babbababbabba\babbababbabbababbababend{eqnarray*}$$

    $$egin{align*}&a\&b\&ba\&bab\&babba\&babbabab\&babbababbabba\&babbababbabbababbababend{align*}$$

      我们可以发现并证明以下性质:

      $1.$  对于任意 $i(i>1)$ ,$s_i$ 为 $s_{i+1}$ 的前缀。

      $2.$  对于任意 $i(i>0)$ ,$s_i$ 为 $s_{i+2}$ 的后缀。

      于是:当斐波那契串长度大于询问串的时候,拼接串时在拼接处产生的新的匹配数的变化周期为 $2$ 。

      于是,对于长度大于询问串的情况,直接搞两个转移矩阵然后快速幂一下就可以了。

      如果长度小于询问串,那么直接回答 $0$ 。

      现在再仔细的看看如何求拼接处产生的匹配数。

      我们记串 $x$ 在 $s$ 中的出现次数为 $KMP(s,x)$。

      则拼接 $s_i,s_{i+1}$ 时,拼接处产生的匹配数为 $KMP(s_{i+1}+s_{i},x)-KMP(s_i,x)-KMP(s_{i+1},x)$ 。

      转移矩阵的构造就不说了,比较基础的,请直接看代码。

      每次从头开始拼接产生第一个长度比当前询问串大的斐波那契串的复杂度会超时,所以我们需要离线按照询问串长度从小到大来。

      这样的复杂度为$O(max{|x|}+mlog m+3^3log k+sum max(|x|,|s_{f(|x|)}|))approx O(mlog m+sum{|x|})$

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1e6+5,mod=1e9+7;
    struct Mat{
    	static const int N=3;
    	int v[N][N];
    	Mat(){}
    	Mat(int x){
    		memset(v,0,sizeof v);
    		if (x==1)
    			for (int i=0;i<N;i++)
    				v[i][i]=1;
    	}
    	void set(int p){
    		v[0][0]=0,v[0][1]=1,v[0][2]=0;
    		v[1][0]=1,v[1][1]=1,v[1][2]=0;
    		v[2][0]=0,v[2][1]=p,v[2][2]=1;
    	}
    	void print(){
    		for (int i=0;i<3;i++,puts(""))
    			for (int j=0;j<3;j++)
    				printf("%3d ",v[i][j]);
    		puts("");
    	}
    	Mat operator * (Mat x){
    		Mat ans(0);
    		for (int i=0;i<N;i++)
    			for (int j=0;j<N;j++)
    				for (int k=0;k<N;k++)
    					ans.v[i][j]=(1LL*v[i][k]*x.v[k][j]+ans.v[i][j])%mod;
    		return ans;
    	}
    	Mat operator ^ (LL y){
    		Mat x=*this,ans(1);
    		while (y){
    			if (y&1LL)
    				ans=ans*x;
    			x=x*x,y>>=1;
    		}
    		return ans;
    	}
    };
    LL k;
    int m;
    struct STR{
    	string s;
    	int id,ans;
    }s[N];
    string s1="a",s2="b",s3="ba",s4="bab";
    bool cmpL(STR a,STR b){
    	if (int(a.s.size())==int(b.s.size()))
    		return a.s<b.s;
    	return a.s.size()<b.s.size();
    }
    bool cmpid(STR a,STR b){
    	return a.id<b.id;
    }
    int Fail[N];
    char S1[N],S2[N];
    int KMP(string &s1,string &s2){
    	int n=s1.size(),m=s2.size();
    	for (int i=1;i<=n;i++)
    		S1[i]=s1[i-1];
    	for (int i=1;i<=m;i++)
    		S2[i]=s2[i-1];
    	Fail[0]=Fail[1]=0;
    	for (int i=2;i<=m;i++){
    		int k=Fail[i-1];
    		while (k&&S2[i]!=S2[k+1])
    			k=Fail[k];
    		if (S2[i]==S2[k+1])
    			k++;
    		Fail[i]=k;
    	}
    	int ans=0,k=0;
    	for (int i=1;i<=n;i++){
    		while (k&&S1[i]!=S2[k+1])
    			k=Fail[k];
    		if (S1[i]==S2[k+1])
    			k++;
    		if (k==m){
    			ans++;
    			k=Fail[k];
    		}
    	}
    	return ans;
    }
    int main(){
    	cin >> k >> m;
    	for (int i=1;i<=m;i++){
    		cin >> s[i].s;
    		s[i].id=i;
    	}
    	sort(s+1,s+m+1,cmpL);
    	int cnt=1;
    	for (int i=1;i<=m;i++){
    		while (cnt<k&&int(s1.size())<int(s[i].s.size())){
    			s1=s4+s3;
    			swap(s1,s2),swap(s2,s3),swap(s3,s4);
    			cnt++;
    		}
    		if (cnt==k){
    			s[i].ans=KMP(s1,s[i].s);
    			continue;
    		}
    		int a=KMP(s1,s[i].s),b=KMP(s2,s[i].s),c=KMP(s3,s[i].s),d=KMP(s4,s[i].s);
    		int del1=c-a-b,del2=d-b-c;
    		Mat st(0),tn1(0),tn2(0);
    		st.v[0][0]=a,st.v[0][1]=b,st.v[0][2]=1;
    		tn1.set(del1),tn2.set(del2);
    		st=st*((tn1*tn2)^((k-cnt)/2));
    		if ((k-cnt)&1)
    			st=st*tn1;
    		s[i].ans=st.v[0][0];
    	}
    	sort(s+1,s+m+1,cmpid);
    	for (int i=1;i<=m;i++)
    		printf("%d
    ",s[i].ans);
    	return 0;
    }
    

      

  • 相关阅读:
    POJ 3695 Rectangles
    POJ 2002 Squares
    linux 查看磁盘空间大小
    keymaster 快捷键管理器
    Pxloader
    as3数据类型检查写法(is/as/typeof/in/instanceof)用法介绍
    javascript的dom选择器
    javascript音频管理方案:SoundManager2
    Morris.js – 画折线图、面积图和环状图的统计图表库
    jsuri 让你方便的处理url
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF117G2.html
Copyright © 2011-2022 走看看