# Generalizing Characters to n-character substrings [duplicate]

I'm doing some frequency analysis on an encrypted plaintext. Tally[] and Characters[] make the basic task quite easy. To count the number of times each character appears (sorted alphabetically; Plaintext has been pre-sanitized), I can carry out

Sort[Tally[Characters[Plaintext], #1[[2]] > #2[[2]] &]


For a more nuanced cryptanalytic approach, I will also want to count the frequency of each digraph (pair of adjacent characters), trigraph (triple of same), ..., n-graph. Tally[] will still work fine here, but the original splitting task (previously performed by Characters[]) is slightly more complicated. I want a functionnGraphList[PTstring_String, Chunklength_Integer] that outputs a list (including repeats) of the Chunklength-graphs found in PTstring. When Chunklength = 1, the function should give identical output to Characters[].

Okay, so the problem isn't really that hard. A quick approach is

nGraphList[PTstring_String, Chunklength_Integer]:=
Table[
StringTake[PTstring, {k, k + Chunklength - 1}],
{k, 1, StringLength[PTstring] - Chunklength + 1}]


The thing drives me crazy, though. It seems like a terribly easy string operation of the sort Mathematica would be able to do in a snap. My solution is clunky, and I can't find any better ideas in the documentation or here. Can you show any different ways to do this? Any smarter or prettier ways?

Related: Clif's question hints at possible usage of stream methods, but 1) I know little on the subject and 2) it looks equally if not more clunky.

• I have marked this as a duplicate. Although slightly different the solutions are easily adaptable; e.g. use StringCases[string, Repeated[_, {n}], Overlaps -> All] for your application. May 4, 2014 at 10:57
• Thanks, @Mr.Wizard. I did spend a while looking, but I never did find that one. Hopefully Rasher and I have been at least some help to posterity :) May 5, 2014 at 19:40

Given string and size, outputs list of character code lists of size, and corresponding x-graph string. Add white-space removal / tally / etc. if desired:

test = "This is a test"

xGraphs[string_, size_] := With[{chars = ToCharacterCode[string]},
{p = Partition[chars, size, 1], FromCharacterCode /@ p}]

xGraphs[test, 2]
xGraphs[test, 4]

(*

{{{84, 104}, {104, 105}, {105, 115}, {115, 32}, {32, 105}, {105,
115}, {115, 32}, {32, 97}, {97, 32}, {32, 116}, {116, 101}, {101,
115}, {115, 116}}, {"Th", "hi", "is", "s ", " i", "is", "s ", " a",
"a ", " t", "te", "es", "st"}}

{{{84, 104, 105, 115}, {104, 105, 115, 32}, {105, 115, 32, 105}, {115,
32, 105, 115}, {32, 105, 115, 32}, {105, 115, 32, 97}, {115, 32,
97, 32}, {32, 97, 32, 116}, {97, 32, 116, 101}, {32, 116, 101,
115}, {116, 101, 115, 116}}, {"This", "his ", "is i", "s is",
" is ", "is a", "s a ", " a t", "a te", " tes", "test"}}

*)


This adds tally of both to output, trims whitespace unless third argument is False:

xGraphs[string_, size_, dropWhiteSpace_: True] :=
Module[{chars =
ToCharacterCode[
If[dropWhiteSpace,
StringReplace[string, WhitespaceCharacter -> ""], string]], p, s},
{p = Partition[chars, size, 1], s = FromCharacterCode /@ p,
Tally[p], Tally[s]}]