551. Student Attendance Record I + Student Attendance Record II
▶ 一个学生的考勤状况是一个字符串,其中各字符的含义是:A 缺勤,L 迟到,P 正常。如果一个学生考勤状况中 A 不超过一个,且没有连续两个 L(L 可以有多个,但是不能连续),则称该学生达标(原文表述:A student could be rewarded if his attendance record doesn't contain more than one 'A' (absent) or more than two continuous 'L' (late). )
▶ 551. 给定一个学生的考勤状况,判断他能否达标。
● 代码,4 ms,简单的计数,最快的解法算法与之相同
class Solution
{
public:
bool checkRecord(string s)
{
int i, a, l;
for (i = a = l = ; i < s.size(); i++)
{
if (s[i] == 'A')
a++;
if (s[i] == 'L')
l++;
else // 出现 A 或 P 时均要清空 L 计数
l = ;
if (a >= || l > )
return false;
}
return true;
}
};
▶ 552. 给定正整数 n,考虑所有长度为 n 的考勤状况(3n 种可能),计算达标的考勤状况数,该数字可能非常大,取关于 109 + 7(竟然是个素数)模作为输出结果
● 自己的代码,29 ms,时间复杂度 O(n),考虑递推数列,一个达标序列只能是以下三种情况:
a[ k ] 表长度为 k,以 P 结尾,不含 A 的达标序列个数
b[ k ] 表长度为 k,以 PL 结尾,不含 A 的达标序列个数(不能是 LLLP 结尾)
c[ k ] 表长度为 k,以 LL 结尾,不含 A 的达标序列个数(不能是 LLL 结尾)
注意到 a[ 1 ] = b[ 1 ] = 1,c[ 1 ] = 0,a[ k ] = a[ k - 1 ] + b[ k - 1 ] + c[ k - 1](任意一种达标序列添个 P),b[ k ] = a[ k - 1 ](P 结尾的达标序列添个 L),c[ k ] = b[ k - 1 ](PL 结尾的达标序列添个 L)
约化后就变成了 a[ 1 ] = 1,a[ 2 ] = 2,a[ 3 ] = 4,a[ n ] = a[ n - 1 ] + a[ n - 2 ] + a[ n - 3 ] ( n ≥ 4 )
对上述数列打表,使用长度为 n+1 的表 f,其中 a[ k ] = f[ k - 1 ],即所有下标有一单位的平移,在代码中注意控制
因为打表序列中 A 至多一个,所以按照 A 的位置分类讨论:
① 不含A,一共有 a[ n ] + b[ n ] + c[ n ] = a[ n + 1 ] 种情况,等于 f[ n ](打表要长一格的原因)
② A 在开头,一共有 a[ n - 1 ] + b[ n - 1 ] + c[ n - 1 ] = a[ n ] 种情况,等于 f[ n - 1 ]
③ A 在结尾,同上,等于 f[ n - 1 ]
④ A 在中间,考虑 A 的左边有长度为 i 的不含 A 的序列,且 A 的右边有长度为 n - i - 1 的不含A的序列,一共是 ( a[ i ] + b[ i ] + c[ i ] ) * ( a[ n - i - 1 ] + b[ n - i - 1 ] + c[ n - i - 1 ] ),即 f[ i ] * f[ n - i - 1 ],
i 在 i = 1 到 i = n - 2 之间求和。
注意每一步取模运算,因为 f 本身是指数级增长的。
class Solution
{
public:
int checkRecord(int n)
{
if (n == )
return ;
const int m = ;
vector<long long> f(n + , );
int i,sum;
for (f[] = , f[] = , f[] = , i = ; i <= n; f[i] = (f[i - ] + f[i - ] + f[i - ]) % m, i++);
for (sum = (f[n] + f[n - ] * ) % m, i = ; i < n - ; i++)
sum = (sum + f[i] * f[n - i - ]) % m;
return sum;
}
};
● 代码,4 ms,使用一个矩阵的幂来计算递推
#define ENTRY(A, i, j) ((A)[(i) * n + (j)]) int64_t* NewMatrix(size_t n)
{
assert(n >= );
int64_t* temp = (int64_t*)calloc(n * n, sizeof(int64_t));
assert(temp != NULL);
return temp;
} void FreeMatrix(int64_t* A)
{
free(A);
} void SetMatrix(int64_t* A, int64_t* B, size_t n)
{
memcpy(A, B, n * n * sizeof(int64_t));
} void IdentityMatrix(int64_t* A, size_t n)// 将方阵 A 赋值为单位方阵
{
int i, j;
for (i = ; i < n; i++)
{
for (j = ; j < n; j++)
ENTRY(A, i, j) = (i == j);
}
} void MatrixMultiply(int64_t* A, int64_t* B, size_t n)// 方阵乘法 A = A * B
{
assert(n >= );
int64_t* C = NewMatrix(n);
int i, j, k;
for (i = ; i < n; ++i)
{
for (j = ; j < n; ++j)
{
ENTRY(C, i, j) = ;
for (k = ; k < n; ++k)
ENTRY(C, i, j) += ENTRY(A, i, k) * ENTRY(B, k, j);
}
}
memcpy(A, C, n * n * sizeof(int64_t));
FreeMatrix(C);
} void MatrixPower(int64_t* C, int64_t* A, size_t n, int m)// 方阵幂 C = A ^ m
{
assert(n >= );
assert(m >= );
int64_t* B = NewMatrix(n);
SetMatrix(B, A, n);
IdentityMatrix(C, n);
for (; m > ; m /= )// 二进制方法
{
if (m % == )
MatrixMultiply(C, B, n);
MatrixMultiply(B, B, n);
}
FreeMatrix(B);
} void MatrixModulus(int64_t* A, size_t n, int64_t modulus)// 方阵逐格求模 A %= m
{
int i, j;
for (i = ; i < n; ++i)
{
for (j = ; j < n; ++j)
ENTRY(A, i, j) = ENTRY(A, i, j) % modulus;
}
} void MatrixModulusPower(int64_t* C, int64_t* A, size_t n, int m, int64_t modulus)// C = A ^ m % modulus
{
assert(n >= );
assert(m >= );
int64_t* B = NewMatrix(n);
SetMatrix(B, A, n);
IdentityMatrix(C, n);
for (; m > ;m/=)
{
if (m % == )
{
MatrixMultiply(C, B, n);
MatrixModulus(C, n, modulus);
}
MatrixMultiply(B, B, n);
MatrixModulus(B, n, modulus);
}
FreeMatrix(B);
} int AttendanceNumber(int m)
{
assert(m >= );
size_t n = ;
int64_t initial_vector[] = { , , , , , };
int64_t modulus = ;
int64_t* A = NewMatrix(n);
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = -;
ENTRY(A, , ) = -;
ENTRY(A, , ) = -;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
ENTRY(A, , ) = ;
int64_t answer = ;
if (m <= )
answer = initial_vector[m];
else
{
int64_t* C = NewMatrix(n);
MatrixModulusPower(C, A, n, m - n + , modulus);
for (size_t i = ; i < n; ++i)
answer += ENTRY(C, n - , i) * initial_vector[i];
answer = (answer % modulus + modulus) % modulus;
FreeMatrix(C);
}
FreeMatrix(A);
return answer;
} #undef ENTRY class Solution
{
public:
int checkRecord(int n)
{
return AttendanceNumber(n);
}
};
● 初版动态规划(MLE),f[ i ][ j ][ k ] 表示长度为 i,至多 j 个 A,至多 k 个连续 L 的序列。所求目标为 f[ n ][ 1 ][ 2 ],转移方程如下,发现可以用矩阵幂来一次性计算 f[ n ]
class Solution
{
public:
int checkRecord(int n)
{
const int MOD = ;
vector<vector<vector<int>>> f(n + , vector<vector<int>>(, vector<int>(3, 0)));
int i, j, k, val;
for (i = ; i < ; i++)
for (j = ; j < ; f[][i][j++] = );
for (i = ; i <= n; i++)
{
for (j = ; j < ; j++)
{
for (k = ; k < ; k++)
{
val = f[i - ][j][]; // P
if (j > ) // A
val = (val + f[i - ][j - ][]) % MOD;
if (k > ) // L
val = (val + f[i - ][j][k - ]) % MOD;
f[i][j][k] = val;
}
}
}
return f[n][][];
}
};
● 改进动态规划,8 ms,直接用矩阵幂来计算
class Solution
{
public:
const int MOD = , M = ;
void mul(vector<vector<int>>& A, vector<vector<int>>& B)
{
vector<vector<int>> temp(M, vector<int>(M, ));
int i, j, k;
for (i = ; i < M; i++)
{
for (j = ; j < M; j++)
{
for (k = ; k < M; k++)
temp[i][j] = (int)((temp[i][j] + (long)A[i][k] * B[k][j]) % MOD);
}
}
A = temp;
return;
}
void pow(vector<vector<int>>& A, int n)
{
vector<vector<int>> temp(M, vector<int>(M, ));
for (int i = ; i < M; temp[i][i] = , i++);
for (; n > ; n /= )
{
if (n % )
mul(temp, A);
mul(A, A);
}
A = temp;
return;
}
int checkRecord(int n)
{
vector<vector<int>> A = \
{
{ , , , , , },
{ , , , , , },
{ , , , , , },
{ , , , , , },
{ , , , , , },
{ , , , , , },
};
pow(A, n + );
return A[][];
}
};
● 代码,深度优先遍历,TLE,说明穷举肯定不行
class Solution
{
public:
int checkRecord(int n)
{
long long res = ;
dfs(n, , , , res);
return res % ;
}
void dfs(int n, int s, int A, int L, long long& res)// 当前共 s 位,有 A 个 A 和 L 个 L
{
if (s == n)
{
res++;
return;
}
dfs(n, s + , A, , res); //add "P"
if (A < )
dfs(n, s + , A + , , res);
if (L <= )
dfs(n, s + , A, L + , res);
}
};
551. Student Attendance Record I + Student Attendance Record II的更多相关文章
- 1 实现添加功能 1.1 定义一个学员类(Student),在Student类中定义姓名、性别和年龄属性,定义有 参数的构造方法来初始化所以的成员属性 1.2 创建学员类对象来存放学员信息,并且为每一个学生对象添加的相应的编号。并将 学员类对象添加到Map<Integer,Student>集合中 1.3 添加完成后,显示所有已添加的学员姓名 1.4 限制年龄文本框只能输入正整数,否则的会采
学生类 package com.lanxi.demo1_3; public class Student { private String name; private String sex; priva ...
- 构建一个学生Student,根据类Student的定义,创建五个该类的对象,输出每个学生的信息,计算并输出这五个学生Java语言成绩的平均值,以及计算并输出他们Java语言成绩的最大值和最小值。
定义一个表示学生信息的类Student,要求如下: (1)类Student的成员变量: sNO 表示学号: sName表示姓名: sSex表示性别: sAge表示年龄: sJava:表示Java课程成 ...
- GitHub Student Developer Pack创建个人网站
链接:https://zhuanlan.zhihu.com/p/20531579 这个开发包里有什么?作为学生开发者,如何最大化利用它的价值? Atom编辑器,GitHub推出的编辑器,和Sublim ...
- C# 9.0新特性详解系列之五:记录(record)和with表达式
1 背景与动机 传统面向对象编程的核心思想是一个对象有着唯一标识,表现为对象引用,封装着随时可变的属性状态,如果你改变了一个属性的状态,这个对象还是原来那个对象,就是对象引用没有因为状态的改变而改变, ...
- C#中的记录(record)
从C#9.0开始,我们有了一个有趣的语法糖:记录(record) 为什么提供记录? 开发过程中,我们往往会创建一些简单的实体,它们仅仅拥有一些简单的属性,可能还有几个简单的方法,比如DTO等等,但是这 ...
- Method and apparatus for encoding data to be self-describing by storing tag records describing said data terminated by a self-referential record
A computer-implemented method and apparatus in a computer system of processing data generated by a f ...
- salesforce 零基础学习(六十二)获取sObject中类型为Picklist的field values(含record type)
本篇引用以下三个链接: http://www.tgerm.com/2012/01/recordtype-specific-picklist-values.html?m=1 https://github ...
- SQL Server 堆表行存储大小(Record Size)
一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 堆表行记录存储格式(Heap) 案例分析(Case) 参考文献(References) 二.背 ...
- Student管理系统
使用三层架构实现Student管理系统,分为Studrnt.Model层,Student.DAL层,Student.BLL层和Student.UI层 步骤分析: 1.在Student.Model添加S ...
随机推荐
- Mac上安装配置和简单使用PostgreSQL(仍然很不懂)
因为想要使用推荐的rails-template.需要使用postgres.并初始化了一个用户postgres,密码是postgres.( e.g. $ createuser -d postgres ) ...
- Python环境管理--virtualenvwrapper
遇到问题: 当最近的开发和部署过程中,多个服务器部署的时候发现对于库和包的管理非常混乱,主要有俩个版本问题: 因为业务需要,代码得分别部署在不同的服务器上面,每次部署的时候都得重复的安装包而且不能确定 ...
- Leetcode 23.Merge Two Sorted Lists Merge K Sorted Lists
Merge Two Sorted Lists Merge two sorted linked lists and return it as a new list. The new list shoul ...
- recv,recvfrom,send,sendto
一般情况下:send(),recv()用于TCP,sendto()及recvfrom()用于UDP 但是send(),recv()也可以用于UDP,sendto()及recvfrom()也可以用于TC ...
- Java HashMap的工作原理
面试的时候经常会遇见诸如:”java中的HashMap是怎么工作的”.”HashMap的get和put内部的工作原理”这样的问题. 本文将用一个简单的例子来解释下HashMap内部的工作原理. 首先我 ...
- Java虚拟机体系结构分析
下图是JAVA虚拟机的结构图: 每个Java虚拟机都有一个类装载子系统,它根据给定的全限定名来装入类型(类或接口).同样,每个Java虚拟机都有一个执行引擎,它负责执行那些包含在被装载类的方法中的指令 ...
- 『转』Dr.Web Security Space 8 – 免费3个月
简短的测试五个问题,任意回答问题,都将获得Dr.Web Security Suite 3个月免费许可证以及大蜘蛛企业安全套件2个月来保护整个公司!活动地址:https://www.drweb.com/ ...
- ES选主策略
ES版本5.6.3 1.整个流程的开始,实在node启动后触发的,Node.java中start()方法,通过调用ZenDiscovery.java中的doStart()方法,之后会调用startIn ...
- C# 解决datatable写入文件内存溢出问题
1.程序生成目标平台设为x64 2.文件写入后主动回收内存
- Java中替换字符串中特殊字符+ 20150921
需求:今天需要将字符串中的" +"转换程"%2B",但是"+"是正则表达式中的特殊字符,使用需要反斜杠转义,具体示范: String a=& ...