leetcode 统计元音字母序列的数目

题目链接
思路一:关系绑定+快速深搜
分析:这里也就是说,a 后面只能是e,e 后面只能是 a 或 i,其他的都是如此,那么也就是说,任何一个字母,后面能跟的字母都是规定好了的。
首先,我们定义一个类,需要记录的信息有这么几个。
1:自己的谁(A E I O U)
2:自己的下标,也就是将五个字母对应一个数字,用来作为后面动态规划的下标
3:后面能跟的字母

那么我们定义的类如下:

private class YY{
		//自己的字母
       char c;
       //对应的下标
       int index;
		//后面能跟的对象
       ArrayList<YY> afterYY = new ArrayList<>();
       
       public YY(char c, int index){
           this.c = c;
           this.index = index;
       }
    }

初始化对象:

	private YY A = new YY('A',0);
    private YY E = new YY('E',1);
    private YY I = new YY('I',2);
    private YY O = new YY('O',3);
    private YY U = new YY('U',4);
    
    {//代码块
        A.afterYY.add(E);

        E.afterYY.add(A);
        E.afterYY.add(I);

        I.afterYY.add(A);
        I.afterYY.add(E);
        I.afterYY.add(O);
        I.afterYY.add(U);

        O.afterYY.add(I);
        O.afterYY.add(U);

        U.afterYY.add(A);
    }

定义一个dp[5][n]二维数组, dp[A.index][j]:表示A后面跟着j个字母一共有几种。
首先我们可以知道:所以的dp[][0],也就是后面0个字母,的总的方案就是1,因为后面没有字母,当前就只有自己,那么肯定是只有一种方案。
状态转移方程:dp[A.index][j] = dp[A.index][j] + dp[A.afterYY.index][j-1]
也就是累加后面每个能选的每个字母的j-1;
代码:

class Solution {

    private class YY{
       char c;
       int index;
       ArrayList<YY> afterYY = new ArrayList<>();
       public YY(char c, int index){
           this.c = c;
           this.index = index;
       }
    }

    private YY A = new YY('A',0);
    private YY E = new YY('E',1);
    private YY I = new YY('I',2);
    private YY O = new YY('O',3);
    private YY U = new YY('U',4);
    private YY[] yy = {A,E,I,O,U};

    private static int NUM = 1000000007;

    {
        A.afterYY.add(E);

        E.afterYY.add(A);
        E.afterYY.add(I);

        I.afterYY.add(A);
        I.afterYY.add(E);
        I.afterYY.add(O);
        I.afterYY.add(U);

        O.afterYY.add(I);
        O.afterYY.add(U);

        U.afterYY.add(A);
    }
    
    public int countVowelPermutation(int n) {
        
        long[][] dp = new long[5][n];
        
        //初始化dp数组
        for(YY y : yy){
            dp[y.index][0] = 1;
        }
        
        //i表示一共有i个字母
        for(int i = 2; i <= n;i++){
            //选了 y  后面还有i-1个,因为是包括y是i个,那么y后面还有i-1个
            for(YY y: yy){
                dp[y.index][i-1] = dfs(dp,y,i-1);
            }
        }
        return (int) ( ( (dp[A.index][n-1] % NUM) + (dp[E.index][n-1] % NUM) + (dp[I.index][n-1] % NUM) + (dp[O.index][n-1] % NUM) + (dp[U.index][n-1] % NUM) ) % NUM);
    }

    public long dfs(long[][] dp, YY y,int i){
    	//如果选择y,后面还有i个字母,不为0的话,说明已经深搜过,那么就不用再搜了
        if(dp[y.index][i]!=0){
            return dp[y.index][i];
        }
		
		//拿到当前字母后面能跟的字母
        ArrayList<YY> afterYY = y.afterYY;
		
		//遍历后面的所有字母
        for(YY afterY : afterYY){
        	//累加后面每个字母的方案
            dp[y.index][i] = ( dp[y.index][i]  + (dfs(dp, afterY,i-1) % NUM)) % NUM;
        }
        return dp[y.index][i];
    }
}

思路二:动态规划
分析:从上面的思路开始,其实我们每次需要用到的只是上一次的结果,也就是 每次求dp[x][i]的时候,只和dp[afterX][i-1]有关,所以我们求[i]只需要记录[i-1]就行。
而且每个字母后面能跟着的字母都是固定的,一共就五种情况。
那么我们只需要写死这五种情况就行。
我们定义两个dp数组。
第一个dp:表示当前每个字母开头,一共能有多少种序列。
第二个dp:表示下一状态,每个字母开头,一共能有多少种序列。
最后将第一个dp每个字母开头加起来就行。
代码:

class Solution {

    private static int NUM = 1000000007;

    public int countVowelPermutation(int n) {
        //a 0   e 1    i 2   o 3   u 4
        long[] dp = new long[5];
        long[] nextdp = new long[5];
        //初始化,也就是说,一共就一个字母,不管谁开头,都只有一种情况
        for(int i = 0; i < 5; i++){
            dp[i] = 1;
        }
	
		//一共有i个字母,也就是说长度为i
        for(int i = 2; i <= n ;i++){
            // a -------e   表示a后面只能跟e,所以相当于在 e的上一轮状态前面加一个a,那么a开头的数量也就是上一轮e开头的数量,下面同理。
            nextdp[0] = (dp[1]) % NUM;

            // e ------- a/i
            nextdp[1] = (dp[0]+dp[2]) % NUM;

            // i -------a/e/o/u
            nextdp[2] = (dp[0]+dp[1]+dp[3]+dp[4]) % NUM;

            //o  ------- i/u
            nextdp[3] = (dp[2]+dp[4]) % NUM;

            // u -------- a
            nextdp[4] = (dp[0]) % NUM;

            System.arraycopy(nextdp,0, dp,0,5);
        }
        return (int)((dp[0] + dp[1] + dp[2] + dp[3] +dp[4]) % NUM);
    }
}

思路三:矩阵快速幂
依照上面的代码,可以改为这样
代码:

class Solution {

    private static int NUM = 1000000007;

    public int countVowelPermutation(int n) {
        //a 0   e 1    i 2   o 3   u 4
        long[] dp = new long[5];
        long[] nextdp = new long[5];
        for(int i = 0; i < 5; i++){
            dp[i] = 1;
        }

        for(int i = 2; i <= n ;i++){
            // a -------e
            nextdp[0] = (dp[0]*0 + dp[1] * 1 + dp[2] * 0+ dp[3] * 0+ dp[4] * 0) % NUM;

            // e ------- a/i
            nextdp[1] = (dp[0]*1 + dp[1] * 0 + dp[2] * 1+ dp[3] * 0+ dp[4] * 0) % NUM;

            // i -------a/e/o/u
            nextdp[2] = (dp[0]*1 + dp[1] * 1 + dp[2] * 0+ dp[3] * 1+ dp[4] * 1) % NUM;

            //o  ------- i/u
            nextdp[3] = (dp[0]*0 + dp[1] * 0 + dp[2] * 1+ dp[3] * 0+ dp[4] * 1) % NUM;

            // u -------- a
            nextdp[4] = (dp[0]*1 + dp[1] * 0 + dp[2] * 0+ dp[3] * 0+ dp[4] * 0) % NUM;

            System.arraycopy(nextdp,0, dp,0,5);
        }
        return (int)((dp[0] + dp[1] + dp[2] + dp[3] +dp[4]) % NUM);
    }
}

可以写成两个矩阵相乘,
nextdp = dp * {
{0, 1, 0, 0, 0},
{1, 0, 1, 0, 0},
{1, 1, 0, 1, 1},
{0, 0, 1, 0, 1},
{1, 0, 0, 0, 0}
};
因为最开始的dp=[1,1,1,1,1]那么最后的dp 应该等于[1,1,1,1,1] * 那个大矩阵的n-1次方。
那么我们只需要快速计算出来这个大矩阵的n-1次方就行。
代码就不贴了。

好好学习。
不打扰是我的温柔。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值