本来可以将命令作为一个抽象类,每个命令写成一个具体类,然后客户端直接调用具体命令类。
对于命令模式,大家可能 心存疑虑,明明是一个很简单的调用逻辑,为什么要做如此的复杂,为什么不直接reciver的excute方法就可以实现功能?调用逻辑复杂,是为了如果后续命令的增加, 能够应对后续需求的变化。简单的只是开发起来方便,但对后续的维护则是困难。除此之外,使用命令模式的另一个好处是可以实现命令记录的功能,可以在调用者里面使用一个数据结构来存储执行过的命令对象,以此可以方便地知道刚刚执行过哪些命令,并可以在需要是恢复。并且可以在调用者中执行日志的记录。
命令模式
也就是说,invoker类是具体的执行者,command类是封装命令,其中数据成员有invoker对象,receiver类是接收者类,client对象直接发送命令给receiver,这样就把请求和具体实现分开了,而且最重要一点是,可以在receiver对象中,创建一个容器对象存储命令,可以实现命令撤销和重做。命令模式解析
当一个请求发出时,低级处理者级别不够,处理不了,就会把这个请求发给更高一级,所以这个请求就像在职责链上传递,直到有一个对象处理它。
Handler类:抽象处理类,包含自己类的对象,代表比此对象高一级对象
Concreate类:具体处理者类。可访问它的后继者,如果可以处理就处理,不能处理,就发给后继者。
缺点:如果处理不当,请求会一直没人处理,陷入死循环
中介者模式
当同事类之间存在相互影响的时候,也就是说,A类对象改变,B类对象也会改变,这时候用中介类来降低耦合。
享元模式
ToDo
输出有多少可能组合。
bool isvalid(vector& mat,int i,int j) {
for(int k = 0; k < i;k++) {
if (mat[k] == j || abs(i-k) == abs(j - mat[k]))
return false;
}
return true;
}
int dfs(vector mat,int i,int n) {
int res = 0;
if (i == n) {
return 1;
}
for (int j = 0; j < n; j++) {
if (isvalid(mat,i,j)) {
mat[i] = j;
res += dfs(mat,i+1,n);
// mat[i] = -1;
}
}
return res;
}
两个字符串,s1,s2,如果两个字符串包含的字符一样,但是顺序可能不一样,就说这两个是变形词。
方法1:1.先判断两个字符串长度是否相同 2. 遍历字符串,用数组保存每个字符出现的次数。 3.比较两个数组每个Index的数值是否相同,出现相同的话就说明不是变形词。
bool helper(string s1,string s2) {
int m = s1.size(),n = s2.size();
if (m != n)
return false;
vector map1(128,0),map2(128,0);
for (auto ch : s1) {
map1[ch]++;
}
for (auto ch : s2)
map2[ch]++;
for (int i = 0; i < 128; i++) {
if (map1[i] != map2[i])
return false;
}
return true;
}
方法2:第一种方法,建立了两个数组,其实只需要一个数组,记录str1中的字符出现频次,然后遍历str2,当出现字符ch,map[ch]-1就可以,当出现<0的情况,返回false;
bool helper(string s1,string s2) {
int m = s1.size(),n = s2.size();
if (m != n)
return false;
vector map1(128,0);
for (auto ch : s1) {
map1[ch]++;
}
for (int i = 0; i < s2.size(); i++) {
map1[s2[i]]--;//主要是这里变化
if (map1[s2[i]] < 0)//判断是否小于0
return false;
}
return true;
}
给定一个字符串,包括数字,字母,各种符号,当出现’-‘号时,看连续’-‘个数,奇数个数代表是负数,偶数个数代表正数,比如说,“A-1BC–12",那么包含数字-1和12.
方法1:遍历字符串,用一个flag标志符号,当出现’-‘,就连续判断之后的字符还是否为’-’;当出现数字时,连续判断之后出现的是否为数字。当数字开始前的符号为’-‘才会用flag标志。
int helper(string s) {
int res = 0;
int n = s.size();
bool flag = true;
for (int i = 0; i < n; i++) {
if (s[i] >= '0' && s[i] <= '9') {
if (i > 0 && s[i-1] != '-')
flag = true;
string temp = "";
while ((i < n) && (s[i] >= '0' && s[i] <= '9')) {
temp += s[i];
i++;
}
i--;
int t = stoi(temp);
res += flag ? t : -t;
flag = true;
}
if (s[i] == '-') {
while ((i < n) && (s[i] == '-')) {
flag = !flag;
i++;
}
i--;
}
}
return res;
}
方法2:从左导向右遍历字符,准确收集数字,然后相加。关键点是怎么把连续的数字组成一个数的过程,比起来上一个方法stoi好很多。
TODO
字符串中会出现k个’0‘,去掉后,返回去掉后的字符串,比如”120000300“,k= 2,则处理后结果为”1200003“。
方法1:
string helper(string s,int k) {
int pos = 0,num = 0;
int n = s.size();
for (int i = 0; i < n; i++) {
if (s[i] == '0') {
num++;
}
else {
if (num == k) {
pos = pos - 2;
}
num = 0;
}
s[pos++] = s[i];
}
if (num == 2)
pos = pos - 2;
return s.substr(0,pos);
}
s1 = “12345”,那么s1的旋转字符串有”23451“,”34512“,”45123“,”51234“,”12345“。所以给定两个字符串s1,s2,判断是不是互为旋转字符串。
方法1:把s2从第一个字符开始,每一个字符都放到最后一位,判断两个字符串是否相同。
bool isSame(string s1,string s2) {
int m = s1.size(),n = s2.size();
if (m != n)
return false;
string temp = s2;
for (int i = 0; i < n; i++) {
if (temp == s1)
return true;
temp = temp.substr(1,n-1) + temp[0];
}
return false;
}
方法2:另str= str2 + str2,然后判断str中是否包含子串str1,因为str包含所有旋转子串。