• css flexbox 总结

    引言

    本文主要对css flexbox的关键点了总结,方便以后在以后的查阅。

    容器的属性

    对于容器需要指定其显示方式为flexbox

    display: flex;
    

    指定flex排列的方向、在排列时是否会换行,使用 flex-flow 可以快速设置二者属性

    flex-direction: row | row-reverse | column | column-reverse;
    flex-wrap: nowrap | wrap | wrap-reverse;
    flex-flow: <'flex-direction'> || <'flex-wrap'>;
    

    用于调整主轴方向的排布(对于 row 来说就是横向,对于 column 来说就是纵向)

    justify-content: flex-start | flex-end | center
    	       | space-between | space-around | space-evenly;
    

    用于调整相交轴方向的行间排布(对于 row 来说就是纵向,对于 column 来说就是横向)

    align-content: flex-start | flex-end | center
    	     | stretch | space-between | space-around;
    

    用于调整相交交轴方向的单行对齐方式。需要注意的是其中 centerbaseline 的区别: 二者都表示居中,而 baseline 会保证所有文字的底边处在同一条线上。

    align-items: flex-start | flex-end | center | baseline | stretch;
    

    条目的属性

    用于调整顺序

    order: <integer>; /* default 0 */
    

    用于调整每个条目的伸展程度

    flex-grow: <number>; /* default 0 */
    

    用于调整每个条目的缩小程度

    flex-shrink: <number>; /* default 1 */
    

    用于调整每个条目的默认尺寸

    flex-basis: <length> | auto; /* default auto */
    

    设置flex属性,排列顺序为 flex-grow , flex-shrink , flex-basis

    flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ];
    

    用于重载容器的 align-items 的设置

    align-self: auto | flex-start | flex-end | center | baseline | stretch;
    
  • emacs的键盘宏(keyboard macro)

    对于一些有规律且重复性的编辑任务, 手动完成十分无聊, 并且需要耗费较长的时间。我在youtube上看到一个使用keyboard marco的 视频 后, 受到很大的启发, 在以后的使用中也会尝试使用宏。我总结了一下视频中的技巧要点,并查阅资料对相关知识点进行了补充和完善。

    有梯子的同学可以去看看,视频地址:https://youtu.be/wFCO__0prCM

    操作指令
    开始记录宏: 命令名称 kmacro-start-macro , 快捷键 C-x-(<f3>
    结束记录宏: 命令名称 kmacro-end-macro , 快捷键 C-x-)<f4>
    执行宏: 命令名称 kmacro-end-and-call-macro , 快捷键 C-x-e , 可以使用 C-u 指定这个宏的执行次数
    清除多余的空格: 命令名称 fixup-whitespace , 这个命令我是第一次见到, 以后可以尝试多用用。

    参考资料

  • 每周算法:最长对称子串

    Description

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

    Example 1:
    Input: "babad"
    Output: "bab"
    Note: "aba" is also a valid answer.

    Example 2:
    Input: "cbbd"
    Output: "bb"

    来源:LeetCode 05 Longest Palindromic Substring

    Solution

    我想到了两种方法:暴力解法、从中心展开

    Approach 1 暴力解法

    暴击解法的时间复杂度为 O(n3) , 找出所有子串的时间复杂度为 O(n2) , 判断一个子串的时间复杂度为 O(n) ; 空间复杂度为 O(1)

    需要注意的是,如果子串过短,就没有必要进行对称性判断了。

    下面是我的代码

    bool isPalindrome(const string& str) {
    
        int len = str.length();
        if (len <=0) {
    	return false;
        }
    
        int head = 0;
        int tail = len - 1;
    
        while (head < tail) {
    	if (str[head] != str[tail]) {
    	    return false;
    	}
    
    	++head;
    	--tail;
        }
    
        return true;
    }
    
    string longestPalindrome(string s) {
    
        string ans = "";
    
        for (size_t i=0; i < s.length(); ++i) {
    	for (size_t j=s.length()-i; j != 0; --j) {
    	    if (ans.length() >= j) {
    		continue;
    	    }
    
    	    string temp = s.substr(i, j);
    	    if (ans.length() < temp.length()
    		&& isPalindrome(temp)) {
    		ans = temp;
    	    }
    	}
        }
    
        return ans;
    }
    

    Approach 2 从中心展开

    从中心展开方法的时间复杂度为 O(n2) , 空间复杂度为 O(1)

    需要注意的是坐标的计算,这个在字符串处理题目中是十分关键的,也是很容易出错的。
    由于单个字符和两个相同字符都可以作为中心,这点需要额外注意一下。

    下面就是我的解法,使用的C++做的。

    string longestPalindrome(string s) {
    
        string ans = "";
    
        for (size_t i=0; i < s.length(); ++i) {
    
    	// 如何确定初始的边界很重要
    	size_t j = i;
    	size_t k = i;
    
    	// 向两边拓展边界
    	while (j-1>=0 && s[j-1]==s[i]) {
    	    --j;
    	}
    
    	while (k+1<s.length() && s[k+1]==s[i]) {
    	    ++k;
    	}
    
    	while (j-1>=0
    	       && k+1<s.length()
    	       && s[j-1]==s[k+1]) {
    	    --j;
    	    ++k;
    	}
    
    	if (k-j+1 > ans.length()) {
    	    ans = s.substr(j, k-j+1);
    	}
        }
    
        return ans;
    }
    

    leetcode上还有一个解法,使用java完成的,它的坐标计算也很有技巧性。

    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
    	int len1 = expandAroundCenter(s, i, i);
    	int len2 = expandAroundCenter(s, i, i + 1);
    	int len = Math.max(len1, len2);
    	if (len > end - start) {
    	    start = i - (len - 1) / 2;
    	    end = i + len / 2;
    	}
        }
        return s.substring(start, end + 1);
    }
    
    private int expandAroundCenter(String s, int left, int right) {
        int L = left, R = right;
        while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
    	L--;
    	R++;
        }
        return R - L - 1;
    }
    

    Approach 3 动态规划(dynamic programming)

    leetcode上还给给出了使用DP解决这个问题的方法。
    我在leetcode上的discuss上找了个java写的解法。

    动态规划的时间复杂度为 O(n2) , 空间复杂度为 O(n2)
    我对dp算法的了解还不多,个人感觉值得思考的是 ij 的变化起点和变化方向。

    public String longestPalindrome(String s) {
      int n = s.length();
      String res = null;
    
      boolean[][] dp = new boolean[n][n];
    
      for (int i = n - 1; i >= 0; i--) {
        for (int j = i; j < n; j++) {
          dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i < 3 || dp[i + 1][j - 1]);
    
          if (dp[i][j] && (res == null || j - i + 1 > res.length())) {
    	res = s.substring(i, j + 1);
          }
        }
      }
    
      return res;
    }
    

    Approach 4 Manacher算法

    这个算法思路实在是新奇,感兴趣的同学可以 去看看

  • 每周算法:最长不含重复字符的子串

    Description

    Given a string, find the length of the longest substring without repeating characters.

    Examples:
    Given "abcabcbb", the answer is "abc", which the length is 3.
    Given "bbbbb", the answer is "b", with the length of 1.
    Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

    来源:LeetCode 03 Longest Substring Without Repeating Characters

    Solution

    好久没刷算法题了,手有点生,没有什么思路,下面的答案是看过提示之后才写出来的。

    Approach 1 暴力解法

    将所有的子串都穷举出来,并对它们进行判断,就能够得到最长子串的长度。这个算法很显然需要耗费很长时间。
    需要注意的是: C++20中才支持 std::set::contains 这个接口(根据cppreference.com)。

    bool isStringWithUniqueString(const string& str)
    {
        // 用于判断字符串是否是具有唯一字符的
        set<char> setChar;
        for (size_t i = 0; i<str.length(); ++i)
        {
    	if (setChar.find(str[i]) != setChar.end())
    	{
    	    return false;
    	}
    
    	setChar.insert(str[i]);
        }
        return true;
    }
    
    int lengthOfLongestSubstring(string s)
    {
        int lengthMax = 0;
    
        for (size_t i=0; i<s.length(); ++i)
        {
    	for (size_t j=i+1; j<=s.length(); ++j)
    	{
    	    // get sub-string
    	    string strTemp = s.substr(i, j-i);
    	    if (isStringWithUniqueString(strTemp))
    	    {
    		lengthMax = max(lengthMax, (int)strTemp.length());
    	    }
    	}
        }
    
        return lengthMax;
    }
    

    Approach 2 滑动窗口

    滑动窗口的概念在字符串处理问题中十分常用,保持子串的左端点不动,不断拓展右侧端点,就能穷举出所有满足条件的子串。
    需要注意的是这句话: lengthMax = max(lengthMax, int(j-i+1));

    1. std::max 并不能同时匹配 intsize_t ,所以需要进行强制类型转换。
    2. 在完成第 j 个字母的验证后,说明包含该字母的子串也满足条件,所以要进行 +1 操作,这个主要是针对只有一个字母的字符串。
    int lengthOfLongestSubstring(string s)
    {
        int lengthMax = 0;
    
        for (size_t i=0; i<s.length(); ++i)
        {
    	// 这个循环目的是,验证从i到j的字符串是否满足条件
    	set<char> setChar;
    
    	for (size_t j=i; j<s.length(); ++j)
    	{
    	    char ch = s[j];
    	    if (setChar.find(ch) != setChar.end())
    	    {
    		break;
    	    }
    	    else
    	    {
    		lengthMax = max(lengthMax, int(j - i + 1));
    	    }
    
    	    setChar.insert(ch);
    	}
        }
    
        return lengthMax;
    }
    

    Approach 3 优化的滑动窗口

    使用 map 对字母出现的位置进行记录,这样在出现相同字母时,就能够从上一次出现的位置向后开始寻找。
    需要注意句话 it->second+1>=i , 需要对位置加1后再比较,这样才能保证 i 的坐标计算正确。

    int lengthOfLongestSubstring(string s)
    {
        int lengthMax = 0;
    
        map<char, size_t> mapPos;
    
        for (size_t i=0, j=0; j<s.length(); ++j)
        {
    	char ch = s[j];
    	map<char, size_t>::iterator it = mapPos.find(ch);
    	if (it != mapPos.end())
    	{
    	    // 这里说明找到了字符上一次出现的位置
    	    if (it->second + 1>= i)
    	    {
    		i = it->second + 1;
    	    }
    	}
    
    	lengthMax = max(lengthMax, int(j - i + 1));
    
    	mapPos[ch] = j;
        }
    
        return lengthMax;
    }
    
  • 提高emacs中浏览和选择操作效率的技巧

    1 引言

    Gaurab Paul的 一篇博文 给了我很大的启发,他详细地介绍了emacs中的相关概念,并提供了许多充满想象力的小技巧。作为emacs的入门级选手确实学到了很多,也拓宽了自己的思路。

    我最初的开发环境是Visual Studio,这一类比较大型的IDE集成了许多功能,但同时也会束缚住使用者的想法。通过这篇文章我感受到的由普通操作指令能组合成的新编辑方式。

    如果英文水平允许的话,非常推荐阅读一下原版的博文,原文中有更加丰富形象的图片示例,无论是跟我一样刚刚入门emacs的新手,还是经验丰富的老兵,都能够从中获得启发。下面,我结合自己的理解和收获谈谈emacs中操作的体会。

    2 pointmarkregion 的概念

    我之前进行代码段复制的操作是十分基础的,用 [email protected] 模拟鼠标按下,方向键模拟鼠标拖动,在鼠标拖动的过程中就形成了一个选区,然后用 M-w 对这个选区进行复制操作,用 C-y 粘贴被复制的内容。

    以上操作带出了几个非常重要的概念 。在emacs中,鼠标光标所在位置被称作 point ;组合键 [email protected] 执行的是 set-mark-command 命令,就是将 point 所在位置标记为 mark ;通过移动光标,也就是移动 point 后,在 pointmark 之间就形成了 region

    3 region 操作技巧

    3.1 调整 region 的大小

    下面就来介绍一个非常重要的命令 exchange-point-and-mark ,这个命令默认被绑定在组合键 C-x C-x 上,从字面意思上很容易理解这条指令的作用,就是交换 markpoint 的位置。这样做的意义在于能够方便地切换 region 的可动边界,这样能够使 region 方便地分别从两端调整大小。
    下面的示例是截取自Paul的博文,需要注意的是,他习惯于使用 C-SPC 调用 set-mark-command

    Lorem ipsum dolor sit amet
          ^ Cursor
    
    
          Point
          |
          Mark
          |
    Lorem ipsum dolor sit amet
          ^ C-spc
    
    
          Mark          Point
          | ----region--|
          |             |
    Lorem ipsum dolor sit amet
    	 move       ^
    	 forward ->
    
    
          Mark          Point
          | ----region--|
          |             |
    Lorem ipsum dolor sit amet
    		    ^
    		    C-x C-x
    
    
          Point         Mark
          | ----region--|
          |             |
    Lorem ipsum dolor sit amet
    
    Point and mark interchanged
    

    3.2 使用 region 进行重复性输入

    对于 region 相关的操作,通常是对已经存在的代码段进行编辑的,如果我们在输入之前就知道有许多字段是需要重复输入的,那么就可以在输入之前设置好 mark ,对输入后形成的 region 完成复制。这个技巧在特定情况能够很大地提升输入效率,但是我个人认为,想要在实战中完成这个操作,还需要保证非常清晰的思路。可以通过下面的示例感受这种操作带来的方便(示例截取自Paul的博文)。

    nil

    下面给出了详细的操作解析

    class
          ^ C-spc => Activate mark
    
    class Foo
    	  ^ M-w => Foo has now been killed (copied)
    
    class Foo extends React.Component<
    				  ^ C-spc => Activate mark
    
    class Foo extends React.Component<
    				  ^ C-y => Yank (paste) Foo
    
    class Foo extends React.Component<FooProps
    					  ^ M-w => FooProps has now been killed (copied)
    
    class Foo extends React.Component<FooProps>
    
    // Later
    interface
    	   ^ C-y => Yank FooProps
    
    interface FooProps {}
    

    3.3 框选一个矩形的 region

    使用 rectangle-mark-mode 命令,默认快捷键 C-x-SPC ,能够框选出一个矩形的 region 。对于矩形 region ,Paul给出的示例是复制 dired 中的多个文件名称,貌似其他合适的使用场景不太多。

    nil

    4 其他插件支持

    有些插件拓展能够实现光标的快速定位,如 helm-swoopavy

    4.1 helm swoop

    从我个人的使用体验来看 helm-swoop 和helm occur的功能十分相似,它们都提供了方便的关键词跳转功能。

    下面的图片来自helm swoop的 主页

    nil

    4.2 avy

    avy 的思路非常独特,这样的跳转和定位让我想起了Chrome浏览器中的Vimium插件,他允许我们使用更少的按键就能跳转到当前buffer中的任意位置,略微遗憾的是它只支持拉丁字母,不过在编写代码的大多数情况下是够用的。

    下面的图片来自Paul的博文

    nil

    5 参考资料

  • shell的输出重定向

    引言

    在linux中借助shell等命令行工具能够很方便地与操作系统交互,可以在shell中将命令或程序的输入结果重定向到特定地方,很方便地实现一些功能。这个技巧十分实用,使用输出重定向能够极大地简化我们的日常操作。

    使用尖括号完成重定向

    示例如下,运行下面的命令能够把 ls 命令的运行结果写入到 ls-output.txt 中。使用 > 会把程序运行时本该输出到 stdout 的内容重定向到指定名称的文件中。

    ls . > ls-output.txt
    

    可以在 > 左面写上数字和 & ,用以标识在重定向时的特殊用法。下面会给出一些特殊用法的实例。

    重定向 stdout 到指定文件中

    ls . 1> ls-output.txt
    

    重定向 stderr 到指定文件中

    ls . 2> ls-error.txt
    

    stdoutstdout 合并再重定向到指定文件中

    ls . 2>&1 ls-output-and-error.txt
    

    以下命令具有相同的效果

    ls . &> ls-output-and-error.txt
    

    将程序的输出丢掉

    可以将输出重定向到一个特殊的文件 /dev/null ,所有写入到这个文件的内容都会被丢弃掉。

    program > /dev/null
    

    将输出追加到指定文件尾部

    使用一个尖括号( > )能够将输出重定向到文件中,在写入文件时会覆盖掉其中的内容。如果想保留文件中的原始内容,则可以用两个尖括号( >> ),这样就能将输出追加到文件的尾部。示例代码如下:

    echo test >> file-output.txt
    

    使用管道完成重定向

    使用管道符号 | 能够将一个程序的输出重定向到另一个程序的输入中去。下面的命令会将 ls 的输出( stdout )重定向到 grep 的输入( stdin )中去。管道命令在linux中是最常见的用法。

    ls | grep <pattern>