diff --git a/Cargo.lock b/Cargo.lock
index 24fc373..1a127ec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,41 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "aoc22"
 version = "0.1.0"
+dependencies = [
+ "regex",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "regex"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
diff --git a/Cargo.toml b/Cargo.toml
index 367d19c..1118341 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+regex = "1"
 
 [[bin]]
 name = "d1p1"
diff --git a/src/bin/d4p1.rs b/src/bin/d4p1.rs
new file mode 100644
index 0000000..7977216
--- /dev/null
+++ b/src/bin/d4p1.rs
@@ -0,0 +1,12 @@
+use aoc22::{day4, util};
+
+pub fn main() {
+    let assignments = day4::parse_assignments(&util::parse_input());
+
+    let overlapping = assignments
+        .iter()
+        .filter(|a| a.0.contains(&a.1) || a.1.contains(&a.0))
+        .count();
+
+    println!("{} assignments overlap", overlapping);
+}
diff --git a/src/day4.rs b/src/day4.rs
new file mode 100644
index 0000000..c257aaa
--- /dev/null
+++ b/src/day4.rs
@@ -0,0 +1,34 @@
+use regex::Regex;
+
+#[derive(Debug)]
+pub struct Assignment {
+    pub lower_bound: u32,
+    pub upper_bound: u32,
+}
+
+impl Assignment {
+    pub fn contains(&self, other: &Self) -> bool {
+        self.lower_bound <= other.lower_bound && self.upper_bound >= other.upper_bound
+    }
+}
+
+pub fn parse_assignments(input: &String) -> Vec<(Assignment, Assignment)> {
+    let re = Regex::new(r"(\d+)-(\d+),(\d+)-(\d+)").unwrap();
+    let mut assignments = Vec::new();
+
+    for line in input.lines() {
+        assert!(re.is_match(line));
+        let captures = re.captures(line).unwrap();
+        let ass1 = Assignment {
+            lower_bound: captures.get(1).unwrap().as_str().parse().unwrap(),
+            upper_bound: captures.get(2).unwrap().as_str().parse().unwrap(),
+        };
+        let ass2 = Assignment {
+            lower_bound: captures.get(3).unwrap().as_str().parse().unwrap(),
+            upper_bound: captures.get(4).unwrap().as_str().parse().unwrap(),
+        };
+        assignments.push((ass1, ass2));
+    }
+
+    assignments
+}
diff --git a/src/lib.rs b/src/lib.rs
index 2ddca43..24d4a18 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
 pub mod day1;
 pub mod day2;
 pub mod day3;
+pub mod day4;
 pub mod util;