6

I need to compactly visualize multiple, possibly intersecting string segments for a given string. For instance, here is pseudocode for all palindrome occurrences in a binary string:

S = 1 0 1 0 0 1 0 0 
    ----- --------- 
    - ----------- - 
      ----- -----   
      - ------- --- 
        - --- - -   
          - -       

I want to render this in LaTeX in a compact way, preferrably with a readable LaTeX code.

My best attempt was using underlines, but it can only cover non-intersecting segments, imposes certain vertical order and is not particularly readable in code:

\begin{align*}
  \let\u\underline
  S = \u{\u{1}\,\u{0}\,\u{1}}\,\u{\u{\u{0}\,\u{0}}\,\u{1}\,\u{\u{0}\,\u{0}}}
\end{align*}

Non-intersecting palindromes of S.

Is there a way to visualize intersecting segments using underscores or other techniques? And if so, can I use nice code layout (as shown above or similar)?

EDIT: an extra challenge is to also enable visualisation of an empty substring between two consecutive symbols. For instance, here are palindromes ending/starting at index 4 in S, including an empty string (which is a degenerate palindrome):

S = 1 0 1 0 0 1 0 0 
           -
          - -
      ----- -----

Note that this can also be done by breaking the string, but it is usually undesired:

S = 1 0 1 0 _ 0 1 0 0
          -   -
      -----   -----
7

The syntax of the proposed command is

\segments[<width>]{<string>}{<segments>;<segments>;...}

where <width> is the width of the cells (default 1em), <string> is the string to typeset and <segments> is a list of cells to underline. Such a list consists of single numbers or <number>-<number> to mean the cells to straddle.

Note that this ignores spaces in both arguments (but ranges should be typed in without spaces), so the example can also be typed in like

\segments{1 0 1 0 0 1 0 0}{
  1, 2, 3, 4, 5, 6, 7, 8 ;
  1-3, 4-5, 7-8 ;
  4-8
}

if preferred.

\documentclass{article}
\usepackage{xparse,array,booktabs}

\ExplSyntaxOn
\NewDocumentCommand{\segments}{O{1em}mm}
 {% #1 = width of cells, #2 = string, #3 = list of underlines
  \group_begin:
  \renewcommand{\arraystretch}{0}
  \fizruk_segments:n { #3 }
  \begin{tabular}[t]{ @{} *{ \tl_count:n { #2 } }{ w{c}{#1} @{} } }
  \seq_set_split:Nnn \l__fizruk_string_seq { } { #2 }
  \seq_use:Nn \l__fizruk_string_seq { & } \\
  \tl_use:N \l__fizruk_segments_tl
  \end{tabular}
  \group_end:
 }

\seq_new:N \l__fizruk_string_seq
\seq_new:N \l__fizruk_stages_seq
\tl_new:N \l__fizruk_segments_tl

\cs_new_protected:Nn \fizruk_segments:n
 {
  \tl_clear:N \l__fizruk_segments_tl
  \seq_set_split:Nnn \l__fizruk_stages_seq { ; } { #1 }
  \seq_map_function:NN \l__fizruk_stages_seq \__fizruk_segment_row:n
 }

\cs_new_protected:Nn \__fizruk_segment_row:n
 {
  \clist_map_function:nN { #1 } \__fizruk_add_segment:n
  \tl_put_right:Nn \l__fizruk_segments_tl { \morecmidrules }
 }

\cs_new_protected:Nn \__fizruk_add_segment:n
 {
  \str_if_in:nnTF { #1 } { - }
   {
    \tl_put_right:Nn \l__fizruk_segments_tl { \cmidrule(l{2pt}r{2pt}){#1} }
   }
   {
    \tl_put_right:Nn \l__fizruk_segments_tl { \cmidrule(l{2pt}r{2pt}){#1-#1} }
   }
 }

\ExplSyntaxOff

\begin{document}

\[
S=\segments{10100100}{1,2,3,4,5,6,7,8;1-3,4-5,7-8; 4-8}
\]

\end{document}

enter image description here

Here's the output of

\[
S=\segments{10100100}{
    1-3,4-8;
    1,2-7,8;
    2-4,5-7;
    2,3-6,7-8;
    3,4-5,6,7;
    4,5
  }
\]

enter image description here

With the following modification, you can insert a line between two symbols, denote it by .3 if you want it to follow the third symbol.

\documentclass{article}
\usepackage{xparse,array,booktabs}

\ExplSyntaxOn
\NewDocumentCommand{\segments}{O{1em}mm}
 {% #1 = width of cells, #2 = string, #3 = list of underlines
  \group_begin:
  \renewcommand{\arraystretch}{0}
  \dim_set:Nn \l__fizruk_cell_dim { #1 }
  \fizruk_segments:n { #3 }
  \tl_show:N \l__fizruk_segments_tl
  \begin{tabular}[t]{ @{} *{ \tl_count:n { #2 } }{ w{c}{#1} @{} } }
  \seq_set_split:Nnn \l__fizruk_string_seq { } { #2 }
  \seq_use:Nn \l__fizruk_string_seq { & } \\
  \tl_use:N \l__fizruk_segments_tl
  \end{tabular}
  \group_end:
 }

\dim_new:N \l__fizruk_cell_dim
\seq_new:N \l__fizruk_string_seq
\seq_new:N \l__fizruk_stages_seq
\tl_new:N \l__fizruk_segments_tl

\cs_new_protected:Nn \fizruk_segments:n
 {
  \tl_set:Nn \l__fizruk_segments_tl { \addlinespace[3pt] }
  \seq_set_split:Nnn \l__fizruk_stages_seq { ; } { #1 }
  \seq_map_function:NN \l__fizruk_stages_seq \__fizruk_segment_row:n
 }

\cs_new_protected:Nn \__fizruk_segment_row:n
 {
  \clist_map_function:nN { #1 } \__fizruk_add_segment:n
  \tl_put_right:Nn \l__fizruk_segments_tl { \morecmidrules }
 }

\cs_new_protected:Nn \__fizruk_add_segment:n
 {
  \str_if_in:nnTF { #1 } { - }
   {
    \tl_put_right:Nx \l__fizruk_segments_tl { \__fizruk_cmidrule:nn { /5 } { #1 } }
   }
   {
    \str_if_in:nnTF { #1 } { . }
     {
      \tl_put_right:Nx \l__fizruk_segments_tl
       {
        \__fizruk_cmidrule:ne { *4/5 } { \tl_tail:n { #1 }-\int_eval:n { \tl_tail:n { #1 }+1 } }
       }
     }
     {
      \tl_put_right:Nx \l__fizruk_segments_tl { \__fizruk_cmidrule:nn { /5 } { #1-#1 } }
     }
   }
 }
\cs_new:Nn \__fizruk_cmidrule:nn
 {
  \exp_not:N\cmidrule(l{\dim_eval:n{\l__fizruk_cell_dim #1}}r{\dim_eval:n{\l__fizruk_cell_dim #1}}){#2}
 }
\cs_generate_variant:Nn \__fizruk_cmidrule:nn { ne }

\ExplSyntaxOff

\begin{document}

\[
S=\segments{10100100}{1,2,3,4,5,6,7,8;1-3,4-5,7-8; 4-8}
\]

\[
S=\segments{10100100}{
    1-3,4-8;
    1,2-7,8;
    2-4,5-7;
    2,3-6,7-8;
    3,4-5,6,7;
    4,5
  }
\]

\[
S = \segments{1 0 1 0 0 1 0 0}{
      .3;
      3,4;
      2-4,5-7
    }
\]
\end{document}

enter image description here

  • The apparent disparity in the thickness of the lines is due to the conversion to PNG. – egreg May 7 at 12:31
  • Looks nice, thanks! Would it also be possible to mark a segment in between letters? That is having a separate underline between two consecutive letters. Sometimes I need to emphasize empty substrings that I build up on. I guess I can do that manually with a simple underline, but I wondered if segments command allows for that. – fizruk May 7 at 13:16
  • 1
    @fizruk You mean something like \segments{1 1 \_ 0 0}{...}? – egreg May 7 at 13:30
  • 1
    @fizruk Added as requested – egreg May 7 at 15:06
  • 1
    @fizruk You should update your TeX system. – egreg May 7 at 16:05
4

I just did three rows here but

enter image description here

\documentclass{article}
\usepackage{booktabs}
\begin{document}

\[
  \begin{array}{r@{}*{10}{c}}
    S={}&1&0&1&0&0&1&0&0 \\
\cmidrule(r){2-4}\cmidrule(l){5-9}
\morecmidrules
\cmidrule(r){2-2}\cmidrule(lr){3-8}\cmidrule(l){9-9}
\morecmidrules
\cmidrule(r){3-5}\cmidrule(l){6-7}
  \end{array}
\]

\end{document}
  • Seems a straight copy from my answer… – egreg May 7 at 9:56
  • 1
    @egreg all my answers are straight copies of yours (even if I write them before you have posted them) – David Carlisle May 7 at 9:58
  • @DavidCarlisle thank you! Can you elaborate what l, r and lr mean in cmidrule? What should I do if I have more that 3 segments in a single layer? – fizruk May 7 at 13:20
  • 1
    @fizruk it means trim the line from the right or left or both, if you do neither they run together with no gaps, actually it may look better to use rl on them all, but I didn't trim the outside edges here, see which you prefer, you can have as many cmidrule in a row as you need. (use (lr) for all the middle ones. – David Carlisle May 7 at 14:07
3

A solution using tikZ. The notation goes as follows: in \ourInfo array, each cell corresponds to: {level, starting character index (including), finish character index(including)}

Output

\documentclass[margin=1cm, tikz]{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[scale=0.5]
    \def\binaryString{{1, 0, 1, 0, 0, 1, 0, 0}}
    %Notation: {level, starting character index (including), finish character index(including)}
    %NOTE! Everyting is zero-based
    \def\ourInfo{{
        % Level 0
        {0,0,2},{0,3,7},
        % Level 1
        {1,0,0},{1,1,6},{1,7,7},
        % Level 2
        {2,1,3},{2,4,7},
        % Level 3
        {3,1,1},{3,2,5},{3,6,7},
        % Level 4
        {4,2,2},{4,3,4},{4,5,5},{4,6,6},
        % Level 5
        {5,3,3},{5,4,4}
    }}
    \pgfmathsetmacro{\length}{15}% Zero based.
    \foreach \i in {0, ..., 7}{
        \draw[] (\i,0) node{\pgfmathparse{\binaryString[\i]}\pgfmathresult};
    }
    \foreach \i in {0, ..., \length}{
        \pgfmathsetmacro{\level}{\ourInfo[\i][0]}
        \pgfmathsetmacro{\start}{\ourInfo[\i][1]}
        \pgfmathsetmacro{\finish}{\ourInfo[\i][2]}
        \draw[thick] (\start-0.3,-0.5-\level/2)--(\finish+0.3,-0.5-\level/2){};
    }
\end{tikzpicture}
\end{document}
  • Thanks! I guess I could omit level and use \i in the last loop. Would it also be possible to mark a segment in between letters? That is having a separate underline between two consecutive letters. Sometimes I need to emphasize empty substrings that I build up on. Could this be as simple as {0, 0.5, 0.5} to place a segment between first and second symbols in a string? – fizruk May 7 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.