• 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;
    }
    

      

  • 相关阅读:
    转载-如何高效的学习技术
    Lc176-第二高的薪水
    Lc4-寻找两个有序数组的中位数
    Lc175-组合两个表
    Lc3-无重复字符的最长子串
    Lc2-俩数相加
    Lc1- 两数之和
    jpa-子查詢
    20191225页面样式
    leetcode二刷结束
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF117G2.html
Copyright © 2020-2023  润新知