Valid Compressed String

Last Updated : 28 Aug, 2026

Given two strings s and t, where s is the original string and t is its compressed form, find if t is a valid compression of s or not.

  • During compression, any sequence of consecutive characters may be replaced by the number of characters removed.
  • If t contains consecutive digits, treat them as a single number. For example, "B32" represents "B" + "32",  not "B" + "3" + "2".

Examples:

Input: s = "GEEKSFORGEEKS", t = "G7G3S"
Output: true
Explanation: 'G' + skip 7 characters ("EEKSFOR") + 'G' + skip 3 characters ("EEK") + 'S' matches s exactly.

Input: s = "DFS", t = "D1D"
Output: false
Explanation: t is not a valid compressed string.

Try It Yourself
redirect icon

[Naive Approach] Using Parsing and Validation - O(n + m) Time and O(m) Space

The main idea is to first parse t into meaningful tokens. A character token must match the corresponding character in s, while a numeric token tells us how many characters to skip.

  • Traverse the compressed string t and parse it into tokens.
  • Store each character as a character token and each complete number as a number token.
  • Traverse the parsed tokens using a pointer j for the original string s.
  • For a number token, move j forward by that many positions; if j exceeds s.length(), return false.
  • For a character token, check whether it matches s[j]; if not, return false, otherwise move j one position forward.
  • After processing all tokens, return true only if j reaches exactly s.length().
C++
#include <bits/stdc++.h>
using namespace std;

bool checkCompressed(string &s, string &t)
{
    // Each token is stored as:
    // {'C', character} -> character token
    // {'N', number}    -> number token
    vector<pair<char, int>> tokens;

    int n = t.size();

    for (int i = 0; i < n; i++)
    {
        // If the current character is a digit,
        // build the complete number from consecutive digits.
        if (isdigit(t[i]))
        {
            int num = 0;

            while (i < n && isdigit(t[i]))
            {
                num = num * 10 + (t[i] - '0');
                i++;
            }

            // Store the number token.
            tokens.push_back({'N', num});

            // The for loop will increment i again,
            // so move it one step back.
            i--;
        }
        else
        {
            // Store the character token.
            tokens.push_back({'C', t[i]});
        }
    }

    int j = 0;

    for (auto &token : tokens)
    {
        // If the token is a number,
        // skip that many characters in s.
        if (token.first == 'N')
        {
            j += token.second;

            // We cannot skip beyond the end of s.
            if (j > s.size())
                return false;
        }
        else
        {
            // If the token is a character,
            // it must match the current character in s.
            if (j >= s.size() || s[j] != token.second)
                return false;

            // Move to the next character.
            j++;
        }
    }

    // The entire original string must be consumed.
    return j == s.size();
}

int main()
{
    string s = "GEEKSFORGEEKS";
    string t = "G7G3S";

    if (checkCompressed(s, t)) cout << "true\n";
    else cout << "false\n";

    return 0;
}
Java
import java.util.*;

// Class used to represent a parsed token.
class Token {
    char type;
    long value;

    Token(char type, long value)
    {
        this.type = type;
        this.value = value;
    }
}

class GFG {
    static boolean checkCompressed(String s, String t)
    {
        // Each token is stored as:
        // {'C', character} -> character token
        // {'N', number}    -> number token
        List<Token> tokens = new ArrayList<>();

        int n = t.length();

        for (int i = 0; i < n; i++) {

            // If the current character is a digit,
            // build the complete number from consecutive
            // digits.
            if (Character.isDigit(t.charAt(i))) {

                long num = 0;

                while (i < n
                       && Character.isDigit(t.charAt(i))) {
                    int digit = t.charAt(i) - '0';

                    // If num is already larger than the
                    // length of s, this number cannot
                    // possibly represent a valid skip.
                    if (num > s.length()) {
                        return false;
                    }

                    num = num * 10 + digit;

                    // If the number itself exceeds the
                    // length of s, it can never be a valid
                    // skip.
                    if (num > s.length()) {
                        return false;
                    }

                    i++;
                }

                // Store the number token.
                tokens.add(new Token('N', num));

                // The for loop will increment i again,
                // so move it one step back.
                i--;
            }
            else {
                // Store the character token.
                tokens.add(new Token('C', t.charAt(i)));
            }
        }

        int j = 0;

        // Validate the tokens against the original string.
        for (Token token : tokens) {

            // If the token is a number,
            // skip that many characters in s.
            if (token.type == 'N') {

                // token.value is guaranteed to be
                // at most s.length().
                j += (int)token.value;

                // We cannot skip beyond the end of s.
                if (j > s.length()) {
                    return false;
                }
            }
            else {

                // If the token is a character,
                // it must match the current character in s.
                if (j >= s.length()
                    || s.charAt(j) != (char)token.value) {

                    return false;
                }

                // Move to the next character.
                j++;
            }
        }

        // The entire original string must be consumed.
        return j == s.length();
    }

    public static void main(String[] args)
    {
        String s = "GEEKSFORGEEKS";
        String t = "G7G3S";

        if (checkCompressed(s, t))
            System.out.println("true");
        else
            System.out.println("false");
    }
}
Python
def checkCompressed(s, t):

    # Each token is stored as:
    # ('C', character) -> character token
    # ('N', number)    -> number token
    tokens = []

    n = len(t)
    i = 0

    while i < n:

        # If the current character is a digit,
        # build the complete number from consecutive digits.
        if t[i].isdigit():

            num = 0

            while i < n and t[i].isdigit():
                num = num * 10 + int(t[i])
                i += 1

            # Store the number token.
            tokens.append(('N', num))

        else:
            # Store the character token.
            tokens.append(('C', t[i]))

            i += 1

    j = 0

    # Validate the tokens against the original string.
    for token_type, value in tokens:

        # If the token is a number,
        # skip that many characters in s.
        if token_type == 'N':

            j += value

            # We cannot skip beyond the end of s.
            if j > len(s):
                return False

        else:

            # If the token is a character,
            # it must match the current character in s.
            if j >= len(s) or s[j] != value:
                return False

            # Move to the next character.
            j += 1

    # The entire original string must be consumed.
    return j == len(s)


# Driver Code
if __name__ == "__main__":
    s = "GEEKSFORGEEKS"
    t = "G7G3S"

    if checkCompressed(s, t):
        print("true")
    else:
        print("false")
C#
using System;
using System.Collections.Generic;

// Class used to represent a parsed token.
class Token {
    public char Type;
    public long Value;

    public Token(char type, long value)
    {
        Type = type;
        Value = value;
    }
}

class GFG {
    static bool checkCompressed(string s, string t)
    {
        // Each token is stored as:
        // {'C', character} -> character token
        // {'N', number}    -> number token
        List<Token> tokens = new List<Token>();

        int n = t.Length;

        for (int i = 0; i < n; i++) {
            
            // If the current character is a digit,
            // build the complete number from consecutive
            // digits.
            if (char.IsDigit(t[i])) {
                long num = 0;

                while (i < n && char.IsDigit(t[i])) {
                    int digit = t[i] - '0';

                    // If num is already larger than the
                    // length of s, this number cannot
                    // possibly represent a valid skip.
                    if (num > s.Length)
                        return false;

                    num = num * 10 + digit;

                    // If the number exceeds the length of
                    // s, it can never be a valid skip.
                    if (num > s.Length)
                        return false;

                    i++;
                }

                // Store the number token.
                tokens.Add(new Token('N', num));

                // The for loop will increment i again,
                // so move it one step back.
                i--;
            }
            else {
                // Store the character token.
                tokens.Add(new Token('C', t[i]));
            }
        }

        int j = 0;

        // Validate the tokens against the original string.
        foreach(Token token in tokens)
        {
            // If the token is a number,
            // skip that many characters in s.
            if (token.Type == 'N') {
                j += (int)token.Value;

                // We cannot skip beyond the end of s.
                if (j > s.Length)
                    return false;
            }
            else {
                // If the token is a character,
                // it must match the current character in s.
                if (j >= s.Length
                    || s[j] != (char)token.Value)
                    return false;

                // Move to the next character.
                j++;
            }
        }

        // The entire original string must be consumed.
        return j == s.Length;
    }

    static void Main()
    {
        string s = "GEEKSFORGEEKS";
        string t = "G7G3S";

        if (checkCompressed(s, t))
            Console.WriteLine("true");
        else
            Console.WriteLine("false");
    }
}
JavaScript
function checkCompressed(s, t)
{
    // Each token is stored as:
    // ["C", character] -> character token
    // ["N", number]    -> number token
    const tokens = [];

    const n = t.length;
    let i = 0;

    while (i < n) {

        // If the current character is a digit,
        // build the complete number from consecutive
        // digits.
        if (t[i] >= "0" && t[i] <= "9") {

            let num = 0;

            while (i < n && t[i] >= "0" && t[i] <= "9") {
                num = num * 10 + Number(t[i]);
                i++;
            }

            // Store the number token.
            tokens.push([ "N", num ]);
        }
        else {

            // Store the character token.
            tokens.push([ "C", t[i] ]);

            i++;
        }
    }

    let j = 0;

    // Validate the tokens against the original string.
    for (const [type, value] of tokens) {

        // If the token is a number,
        // skip that many characters in s.
        if (type === "N") {

            j += value;

            // We cannot skip beyond the end of s.
            if (j > s.length)
                return false;
        }
        else {

            // If the token is a character,
            // it must match the current character in s.
            if (j >= s.length || s[j] !== value)
                return false;

            // Move to the next character.
            j++;
        }
    }

    // The entire original string must be consumed.
    return j === s.length;
}

// Driver Code
const s = "GEEKSFORGEEKS";
const t = "G7G3S";

if (checkCompressed(s, t))
    console.log("true");
else
    console.log("false");

Output
true

[Expected Approach] Using Two Pointer Approach - O(m) Time and O(1) Space

The main idea is to parse and validate t at the same time, so there is no need to store the tokens separately.

We maintain a pointer j in the original string s. When we encounter consecutive digits in t, we combine them into one number and skip that many characters in s.

When we encounter a character, it must match the current character of s. If any mismatch occurs or a skip goes beyond the end of s, the compression is invalid.

  • Initialize a pointer j = 0 for the original string s and traverse the compressed string t.
  • If t[i] is a digit, combine all consecutive digits to form the complete skip count.
  • Move j forward by the skip count; if j exceeds s.length(), return false.
  • If t[i] is a character, check whether it matches s[j]; if not, return false, otherwise increment j.
  • Continue until the entire compressed string t is processed.
  • Finally, return true only if j == s.length(), meaning the compression consumes the original string exactly.
C++
#include <bits/stdc++.h>
using namespace std;

bool checkCompressed(string &s, string &t)
{
    int j = 0; // Pointer for the original string s
    int n = t.size();

    for (int i = 0; i < n; i++)
    {
        // If the current character is a digit,
        // build the complete number from consecutive digits.
        if (isdigit(t[i]))
        {
            long long skip = 0;

            while (i < n && isdigit(t[i]))
            {
                int digit = t[i] - '0';

                // If skip is already greater than s.size(),
                // this number cannot be valid.
                if (skip > s.size())
                    return false;

                skip = skip * 10 + digit;

                // No valid compression can skip more
                // characters than the size of s.
                if (skip > s.size())
                    return false;

                i++;
            }

            // Skip the required number of characters.
            j += skip;

            // The skip cannot go beyond the end of s.
            if (j > s.size())
                return false;

            // The for loop will increment i again,
            // so move it one step back.
            i--;
        }
        else
        {
            // Current character must match s[j].
            if (j >= s.size() || s[j] != t[i])
                return false;

            // Move to the next character in s.
            j++;
        }
    }

    // The entire original string must be consumed.
    return j == s.size();
}

int main()
{
    string s = "GEEKSFORGEEKS";
    string t = "G7G3S";

    if (checkCompressed(s, t))
        cout << "true\n";
    else
        cout << "false\n";

    return 0;
}
Java
class GFG {
    static boolean checkCompressed(String s, String t)
    {
        int j = 0; // Pointer for the original string s
        int n = t.length();

        for (int i = 0; i < n; i++) {

            // If the current character is a digit,
            // build the complete number from consecutive
            // digits.
            if (Character.isDigit(t.charAt(i))) {

                long skip = 0;

                while (i < n
                       && Character.isDigit(t.charAt(i))) {

                    int digit = t.charAt(i) - '0';

                    // If skip is already greater than
                    // s.length(), this number cannot be
                    // valid.
                    if (skip > s.length())
                        return false;

                    skip = skip * 10 + digit;

                    // No valid compression can skip more
                    // characters than the size of s.
                    if (skip > s.length())
                        return false;

                    i++;
                }

                // Skip the required number of characters.
                j += (int)skip;

                // The skip cannot go beyond the end of s.
                if (j > s.length())
                    return false;

                // The for loop will increment i again,
                // so move it one step back.
                i--;
            }
            else {

                // Current character must match s[j].
                if (j >= s.length()
                    || s.charAt(j) != t.charAt(i))
                    return false;

                // Move to the next character in s.
                j++;
            }
        }

        // The entire original string must be consumed.
        return j == s.length();
    }

    public static void main(String[] args)
    {
        String s = "GEEKSFORGEEKS";
        String t = "G7G3S";

        if (checkCompressed(s, t))
            System.out.println("true");
        else
            System.out.println("false");
    }
}
Python
def checkCompressed(s, t):

    j = 0  # Pointer for the original string s
    n = len(t)
    i = 0

    while i < n:

        # If the current character is a digit,
        # build the complete number from consecutive digits.
        if t[i].isdigit():

            skip = 0

            while i < n and t[i].isdigit():

                digit = int(t[i])

                # If skip is already greater than the
                # length of s, this number cannot be valid.
                if skip > len(s):
                    return False

                skip = skip * 10 + digit

                # No valid compression can skip more
                # characters than the size of s.
                if skip > len(s):
                    return False

                i += 1

            # Skip the required number of characters.
            j += skip

            # The skip cannot go beyond the end of s.
            if j > len(s):
                return False

        else:

            # Current character must match s[j].
            if j >= len(s) or s[j] != t[i]:
                return False

            # Move to the next character in s.
            j += 1
            i += 1

    # The entire original string must be consumed.
    return j == len(s)


# Driver Code
if __name__ == "__main__":
    s = "GEEKSFORGEEKS"
    t = "G7G3S"

    if checkCompressed(s, t):
        print("true")
    else:
        print("false")
C#
using System;

class GFG {
    static bool checkCompressed(string s, string t)
    {
        int j = 0; // Pointer for the original string s
        int n = t.Length;

        for (int i = 0; i < n; i++) {
            
            // If the current character is a digit,
            // build the complete number from consecutive
            // digits.
            if (char.IsDigit(t[i])) {
                long skip = 0;

                while (i < n && char.IsDigit(t[i])) {
                    int digit = t[i] - '0';

                    // If skip is already greater than
                    // the length of s, this number cannot
                    // be valid.
                    if (skip > s.Length)
                        return false;

                    skip = skip * 10 + digit;

                    // No valid compression can skip more
                    // characters than the size of s.
                    if (skip > s.Length)
                        return false;

                    i++;
                }

                // Skip the required number of characters.
                j += (int)skip;

                // The skip cannot go beyond the end of s.
                if (j > s.Length)
                    return false;

                // The for loop will increment i again,
                // so move it one step back.
                i--;
            }
            else {
                // Current character must match s[j].
                if (j >= s.Length || s[j] != t[i])
                    return false;

                // Move to the next character in s.
                j++;
            }
        }

        // The entire original string must be consumed.
        return j == s.Length;
    }

    static void Main()
    {
        string s = "GEEKSFORGEEKS";
        string t = "G7G3S";

        if (checkCompressed(s, t))
            Console.WriteLine("true");
        else
            Console.WriteLine("false");
    }
}
JavaScript
function checkCompressed(s, t)
{
    let j = 0; // Pointer for the original string s
    let n = t.length;

    for (let i = 0; i < n; i++) {

        // If the current character is a digit,
        // build the complete number from consecutive
        // digits.
        if (t[i] >= "0" && t[i] <= "9") {

            let skip = 0;

            while (i < n && t[i] >= "0" && t[i] <= "9") {

                let digit = Number(t[i]);

                // If skip is already greater than
                // the length of s, this number cannot be
                // valid.
                if (skip > s.length)
                    return false;

                skip = skip * 10 + digit;

                // No valid compression can skip more
                // characters than the size of s.
                if (skip > s.length)
                    return false;

                i++;
            }

            // Skip the required number of characters.
            j += skip;

            // The skip cannot go beyond the end of s.
            if (j > s.length)
                return false;

            // The for loop will increment i again,
            // so move it one step back.
            i--;
        }
        else {

            // Current character must match s[j].
            if (j >= s.length || s[j] !== t[i])
                return false;

            // Move to the next character in s.
            j++;
        }
    }

    // The entire original string must be consumed.
    return j === s.length;
}

// Driver Code
const s = "GEEKSFORGEEKS";
const t = "G7G3S";

if (checkCompressed(s, t))
    console.log("true");
else
    console.log("false");

Output
true
Comment