• 字符串普及题 2


    几天前我还会写普及组题,现在已经不会写了


    题意

      给你一个仅由字符 ( ext{H,T}) 构成的字符串 (S)
      有一个初始为空的字符串 (T),每次随机在 (T) 的末尾添加 ( ext{H})( ext{T})
      问当 (S)(T) 的后缀时,在末尾添加字符的期望次数(即 (T) 的期望长度)。
      (|S|le 10^6)

    题解

      真的是个普及组题

      不考虑构建 (T) 串,只考虑构建 (S) 串。
      那么问题相当于转化为:初始时你在 (s) 串的第 (0) 位,当你在第 (i) 位时,有 (0.5) 的概率走到第 (i+1) 位,另外 (0.5) 的概率走到第 (fail[i+1]) 位。求走到第 (|S|) 位的期望步数。
      (fail[i]) 表示将第 (i) 位字符反转后,(S) 串自己对自己做 KMP 时第 (i) 位的 (fail) 指针指向的位置。两次 KMP 预处理即可。
      然后这就是个普及组的期望 (dp)……

    方法 1

      设 (f[i]) 表示第一次从第 (i) 位走到第 (i+1) 位的期望步数。
      此时转移为 (f[i] = 0.5 imes 1 + 0.5 imes (1+f[fail[i+1]]+f[fail[i+1]+1]+...+f[i-1]+f[i]))
      后面那一长串的意思就是 到达上一个匹配位置,再逐步走过来。
      由于是递推,前缀和优化即可。

    方法 2

      另外一种方法:
      设 (f[i]) 表示第 (i) 位走到第 (n) 位的期望步数。
      转移为 (f[i] = 0.5 imes f[i+1] + 0.5 imes f[fail[i+1]] + 1)
      移项得 (f[i+1] = 2 imes f[i] - f[fail[i+1]] - 2)
      好像是带环的解方程,不能直接倒推 (f)
      但不难发现,这种倒推在起点处的性质很好,即 (f[0] = 0.5 imes f[1] + 0.5 imes f[0] + 1),即 (f[1]=f[0]-2)
      由于对于任意的 (ige 2)(f[i]) 最多只与 (2) 个满足 (jlt i)(f[j]) 有关,所以从 (f[0])(f[1]) 出发可以递推出其它所有点的相关信息。
      下面考虑什么是“相关信息”。
      我们知道终点处 (f[|S|]=0)。如果能得到一个关于 (f[0])(f[|S|]) 的等式就好了,这样我们可以直接算出 (f[0])
      于是把每个位置用 (a imes f[0]+b) 表示出来。正着扫一遍 (S) 串递推出来所有位置的系数 (a)(b),得到 (f[n]) 的两个系数后就可以得到关于 (f[0])(f[|S|]) 的等式了。

      关于第二种做法,后来我想到一个问题:最后一步算 (f[0]) 要做除法(除以 (a[n])),不会得小数么?
      然后把所有位置的 (a) 都输出出来,发现全是 (1)……
      下意识地看了下转移式,突然明白了些什么……
      所以这题可以不记系数 (a)……

      本质就是解方程高斯消元

    #include<bits/stdc++.h>
    #define N 1000010
    #define mod 1000000007
    using namespace std;
    inline int read(){
        int x=0; bool f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
        for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
        if(f) return x; return 0-x;
    }
    char s[N];
    int n,fail[N],_fail[N],g[N];
    void KMP(){
        int j=0;
        for(int i=2; i<=n; ++i){
            while(j && s[i]!=s[j+1]) j=fail[j];
            if(s[i]==s[j+1]) ++j;
            fail[i]=j;
        }
        j=0;
        for(int i=2; i<=n; ++i){
            while(j && s[i]==s[j+1]) j=fail[j];
            if(s[i]!=s[j+1]) ++j;
            _fail[i]=j, j=fail[i];
        }
    }
    int main(){
        scanf("%s",s+1); n=strlen(s+1);
        KMP();
        g[1]=mod-2;
        for(int i=2; i<=n; ++i) g[i] = ((g[i-1]*2%mod - g[_fail[i]] - 2) % mod + mod) % mod;
        cout<<mod-g[n]<<endl;
        return 0;
    }
    
  • 相关阅读:
    Windows CMD 配置 启动 服务
    Starting a Service
    socket 相关文章
    Qt GUI程序带命令行
    socket 双向
    winsock Options
    winsock 主动切断连接 Connection Setup and Teardown
    在 u 开头的单词前面,用 a 还是 an
    Web自动化----搭建基本环境
    Python----yield 生成器
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/190813c.html
Copyright © 2020-2023  润新知